This commit is contained in:
Andrej730
2025-02-25 11:43:29 +05:00
parent 5f4098e5d4
commit 2f6ae1745f
87 changed files with 739 additions and 448 deletions
+1 -1
View File
@@ -137,7 +137,7 @@ class IfcExporter:
return checksum != tool.Geometry.get_material_checksum(obj) return checksum != tool.Geometry.get_material_checksum(obj)
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]: def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = self.file.by_id(tool.Blender.get_object_bim_props(obj).ifc_definition_id)
if tool.Geometry.is_scaled(obj): if tool.Geometry.is_scaled(obj):
bpy.ops.bim.update_representation(obj=obj.name) bpy.ops.bim.update_representation(obj=obj.name)
# update_representation might not apply scale if the object has openings # update_representation might not apply scale if the object has openings
+7 -6
View File
@@ -64,14 +64,15 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
refresh_ui_data() refresh_ui_data()
return return
if not obj.BIMObjectProperties.ifc_definition_id: props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id:
return return
if obj.BIMObjectProperties.is_renaming: if props.is_renaming:
obj.BIMObjectProperties.is_renaming = False props.is_renaming = False
return return
element = tool.Ifc.get().by_id(obj.BIMObjectProperties.ifc_definition_id) element = tool.Ifc.get().by_id(props.ifc_definition_id)
if "/" in obj.name: if "/" in obj.name:
object_name = obj.name object_name = obj.name
element_name = obj.name.split("/", 1)[1] element_name = obj.name.split("/", 1)[1]
@@ -87,8 +88,8 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
if not element.is_a("IfcRoot"): if not element.is_a("IfcRoot"):
return return
element.Name = element_name element.Name = element_name
if obj.BIMObjectProperties.collection: if props.collection:
obj.BIMObjectProperties.collection.name = object_name props.collection.name = object_name
refresh_ui_data() refresh_ui_data()
+4 -2
View File
@@ -267,7 +267,8 @@ class IfcStore:
if element.is_a("IfcSurfaceStyle"): if element.is_a("IfcSurfaceStyle"):
obj.BIMStyleProperties.ifc_definition_id = element.id() obj.BIMStyleProperties.ifc_definition_id = element.id()
else: else:
obj.BIMObjectProperties.ifc_definition_id = element.id() props = tool.Blender.get_object_bim_props(obj)
props.ifc_definition_id = element.id()
tool.Ifc.setup_listeners(obj) tool.Ifc.setup_listeners(obj)
@@ -407,7 +408,8 @@ class IfcStore:
if isinstance(obj, bpy.types.Material): if isinstance(obj, bpy.types.Material):
obj.BIMStyleProperties.ifc_definition_id = 0 obj.BIMStyleProperties.ifc_definition_id = 0
else: # bpy.types.Object else: # bpy.types.Object
obj.BIMObjectProperties.ifc_definition_id = 0 props = tool.Blender.get_object_bim_props(obj)
props.ifc_definition_id = 0
@staticmethod @staticmethod
def execute_ifc_operator( def execute_ifc_operator(
+4 -2
View File
@@ -933,7 +933,8 @@ class IfcImporter:
self.project = {"ifc": project} self.project = {"ifc": project}
obj = tool.Ifc.get_object(project) obj = tool.Ifc.get_object(project)
if obj: if obj:
self.project["blender"] = obj.BIMObjectProperties.collection props = tool.Blender.get_object_bim_props(obj)
self.project["blender"] = props.collection
self.has_existing_project = True self.has_existing_project = True
return return
self.project["blender"] = bpy.data.collections.new( self.project["blender"] = bpy.data.collections.new(
@@ -943,7 +944,8 @@ class IfcImporter:
obj.hide_select = True obj.hide_select = True
self.project["blender"].objects.link(obj) self.project["blender"].objects.link(obj)
self.project["blender"].BIMCollectionProperties.obj = obj self.project["blender"].BIMCollectionProperties.obj = obj
obj.BIMObjectProperties.collection = self.collections[project.GlobalId] = self.project["blender"] props = tool.Blender.get_object_bim_props(obj)
props.collection = self.collections[project.GlobalId] = self.project["blender"]
def create_styles(self) -> None: def create_styles(self) -> None:
for style in self.file.by_type("IfcSurfaceStyle"): for style in self.file.by_type("IfcSurfaceStyle"):
+6 -6
View File
@@ -34,9 +34,9 @@ class BIM_PT_aggregate(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id: if not props.ifc_definition_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
@@ -66,9 +66,9 @@ class BIM_PT_aggregate(Panel):
col.enabled = False col.enabled = False
op = col.operator("bim.aggregate_assign_object", icon="CHECKMARK") op = col.operator("bim.aggregate_assign_object", icon="CHECKMARK")
if props.relating_object: if props.relating_object:
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id op.relating_object = tool.Blender.get_object_bim_props(props.relating_object).ifc_definition_id
elif props.related_object: elif props.related_object:
op.related_object = props.related_object.BIMObjectProperties.ifc_definition_id op.related_object = tool.Blender.get_object_bim_props(props.related_object).ifc_definition_id
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="") row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
return return
else: else:
@@ -115,9 +115,9 @@ class BIM_PT_linked_aggregate(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id: if not props.ifc_definition_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
@@ -316,7 +316,8 @@ class ColourByRelatedBuildingElement(bpy.types.Operator):
def _execute(self, context): def _execute(self, context):
for obj in context.visible_objects: for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id: props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id:
continue continue
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element.is_a("IfcRelSpaceBoundary"): if not element.is_a("IfcRelSpaceBoundary"):
+8 -6
View File
@@ -52,9 +52,9 @@ class BIM_PT_Boundary(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id: if not props.ifc_definition_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
@@ -65,7 +65,7 @@ class BIM_PT_Boundary(Panel):
def draw(self, context): def draw(self, context):
obj = context.active_object obj = context.active_object
assert obj assert obj
props = obj.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
boundary = ifc_file.by_id(props.ifc_definition_id) boundary = ifc_file.by_id(props.ifc_definition_id)
self.bprops = tool.Boundary.get_object_boundary_props(obj) self.bprops = tool.Boundary.get_object_boundary_props(obj)
@@ -128,9 +128,9 @@ class BIM_PT_SpaceBoundaries(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id: if not props.ifc_definition_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
@@ -145,7 +145,9 @@ class BIM_PT_SpaceBoundaries(Panel):
if not SpaceBoundariesData.is_loaded: if not SpaceBoundariesData.is_loaded:
SpaceBoundariesData.load() SpaceBoundariesData.load()
self.props = context.active_object.BIMObjectProperties obj = context.active_object
assert obj
self.props = tool.Blender.get_object_bim_props(obj)
self.ifc_file = tool.Ifc.get() self.ifc_file = tool.Ifc.get()
row = self.layout.row() row = self.layout.row()
row.operator("bim.load_space_boundaries") row.operator("bim.load_space_boundaries")
+9
View File
@@ -20,6 +20,7 @@ import bpy
from bonsai.bim.module.model.data import AuthoringData from bonsai.bim.module.model.data import AuthoringData
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from math import pi from math import pi
from typing import TYPE_CHECKING
class BIMCadProperties(PropertyGroup): class BIMCadProperties(PropertyGroup):
@@ -31,3 +32,11 @@ class BIMCadProperties(PropertyGroup):
gable_roof_edge_angle: bpy.props.FloatProperty( gable_roof_edge_angle: bpy.props.FloatProperty(
name="Gable Roof Edge Angle", default=pi / 2, soft_min=0, soft_max=pi / 2, subtype="ANGLE" name="Gable Roof Edge Angle", default=pi / 2, soft_min=0, soft_max=pi / 2, subtype="ANGLE"
) )
if TYPE_CHECKING:
resolution: int
radius: float
distance: float
x: float
y: float
gable_roof_edge_angle: float
+12 -6
View File
@@ -146,7 +146,7 @@ class CadTool(WorkSpaceTool):
if ( if (
(RailingData.is_loaded or not RailingData.load()) (RailingData.is_loaded or not RailingData.load())
and RailingData.data["pset_data"] and RailingData.data["pset_data"]
and obj.BIMRailingProperties.is_editing_path and tool.Model.get_railing_props(obj).is_editing_path
): ):
add_header_apply_button( add_header_apply_button(
layout, layout,
@@ -159,7 +159,7 @@ class CadTool(WorkSpaceTool):
elif ( elif (
(RoofData.is_loaded or not RoofData.load()) (RoofData.is_loaded or not RoofData.load())
and RoofData.data["pset_data"] and RoofData.data["pset_data"]
and obj.BIMRoofProperties.is_editing_path and tool.Model.get_roof_props(obj).is_editing_path
): ):
add_header_apply_button( add_header_apply_button(
layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context
@@ -195,12 +195,14 @@ class CadHotkey(bpy.types.Operator):
return operator.description or "" return operator.description or ""
def execute(self, context): def execute(self, context):
self.props = context.scene.BIMCadProperties self.props = tool.Cad.get_cad_props()
getattr(self, f"hotkey_{self.hotkey}")() getattr(self, f"hotkey_{self.hotkey}")()
return {"FINISHED"} return {"FINISHED"}
def draw(self, context): def draw(self, context):
props = context.scene.BIMCadProperties props = tool.Cad.get_cad_props()
obj = context.active_object
if self.hotkey == "S_C": if self.hotkey == "S_C":
if tool.Geometry.is_profile_object_active(): if tool.Geometry.is_profile_object_active():
row = self.layout.row() row = self.layout.row()
@@ -226,7 +228,7 @@ class CadHotkey(bpy.types.Operator):
elif ( elif (
(RoofData.is_loaded or not RoofData.load()) (RoofData.is_loaded or not RoofData.load())
and RoofData.data["pset_data"] and RoofData.data["pset_data"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path and tool.Model.get_roof_props(obj).is_editing_path
): ):
self.layout.row().prop(props, "gable_roof_edge_angle") self.layout.row().prop(props, "gable_roof_edge_angle")
@@ -281,13 +283,17 @@ class CadHotkey(bpy.types.Operator):
bpy.ops.bim.edit_extrusion_axis() bpy.ops.bim.edit_extrusion_axis()
def hotkey_S_R(self): def hotkey_S_R(self):
obj = bpy.context.active_object
if not obj:
return
if tool.Geometry.is_profile_object_active(): if tool.Geometry.is_profile_object_active():
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bpy.ops.bim.add_rectangle(x=self.props.x / si_conversion, y=self.props.y / si_conversion) bpy.ops.bim.add_rectangle(x=self.props.x / si_conversion, y=self.props.y / si_conversion)
elif ( elif (
(RoofData.is_loaded or not RoofData.load()) (RoofData.is_loaded or not RoofData.load())
and RoofData.data["pset_data"] and RoofData.data["pset_data"]
and bpy.context.active_object.BIMRoofProperties.is_editing_path and tool.Model.get_roof_props(obj).is_editing_path
): ):
bpy.ops.bim.set_gable_roof_edge_angle(angle=self.props.gable_roof_edge_angle) bpy.ops.bim.set_gable_roof_edge_angle(angle=self.props.gable_roof_edge_angle)
@@ -316,7 +316,8 @@ class SelectIfcClashResults(bpy.types.Operator):
global_ids.extend([clash["a_global_id"], clash["b_global_id"]]) global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
for obj in context.visible_objects: for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id: props = tool.Blender.get_object_bim_props(obj)
if not props.ifc_definition_id:
continue continue
ifc_file = "" ifc_file = ""
@@ -335,7 +336,7 @@ class SelectIfcClashResults(bpy.types.Operator):
element_file = self.file element_file = self.file
try: try:
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = element_file.by_id(props.ifc_definition_id)
except: except:
continue continue
@@ -161,8 +161,12 @@ class AssignConstraint(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)] products = [
self.file.by_id(obj_id)
for obj in objs
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
]
if products: if products:
ifcopenshell.api.run( ifcopenshell.api.run(
"constraint.assign_constraint", "constraint.assign_constraint",
@@ -184,8 +188,12 @@ class UnassignConstraint(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects
products = [self.file.by_id(obj_id) for obj in objs if (obj_id := obj.BIMObjectProperties.ifc_definition_id)] products = [
self.file.by_id(obj_id)
for obj in objs
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
]
if products: if products:
ifcopenshell.api.run( ifcopenshell.api.run(
"constraint.unassign_constraint", "constraint.unassign_constraint",
@@ -79,18 +79,18 @@ class BIM_PT_object_constraints(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id): props = tool.Blender.get_object_bim_props(obj)
return False return bool(tool.Ifc.get_object_by_identifier(props.ifc_definition_id))
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
def draw(self, context): def draw(self, context):
if not ObjectConstraintsData.is_loaded: if not ObjectConstraintsData.is_loaded:
ObjectConstraintsData.load() ObjectConstraintsData.load()
obj = context.active_object obj = context.active_object
self.oprops = obj.BIMObjectProperties assert obj
self.oprops = tool.Blender.get_object_bim_props(obj)
self.sprops = context.scene.BIMConstraintProperties self.sprops = context.scene.BIMConstraintProperties
self.props = obj.BIMObjectConstraintProperties self.props = obj.BIMObjectConstraintProperties
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
@@ -740,15 +740,14 @@ class LoadProductCostItems(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not tool.Ifc.get() or not (obj := context.active_object) or not (obj.BIMObjectProperties.ifc_definition_id): if not tool.Ifc.get() or not (obj := context.active_object) or not (tool.Blender.get_ifc_definition_id(obj)):
cls.poll_message_set("No IFC object is active.") cls.poll_message_set("No IFC object is active.")
return False return False
return True return True
def execute(self, context): def execute(self, context):
core.load_product_cost_items( obj = context.active_object
tool.Cost, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id) core.load_product_cost_items(tool.Cost, product=tool.Ifc.get_entity(obj))
)
return {"FINISHED"} return {"FINISHED"}
@@ -18,9 +18,13 @@
import bpy import bpy
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from typing import TYPE_CHECKING
class BIMCoveringProperties(PropertyGroup): class BIMCoveringProperties(PropertyGroup):
ceiling_height: bpy.props.FloatProperty( ceiling_height: bpy.props.FloatProperty(
name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height" name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height"
) )
if TYPE_CHECKING:
ceiling_height: float
@@ -55,7 +55,7 @@ class CoveringToolUI:
def draw(cls, context, layout, ifc_element_type=None): def draw(cls, context, layout, ifc_element_type=None):
cls.layout = layout cls.layout = layout
cls.props = tool.Model.get_model_props() cls.props = tool.Model.get_model_props()
cls.covering_props = context.scene.BIMCoveringProperties cls.covering_props = tool.Covering.get_covering_props()
row = cls.layout.row(align=True) row = cls.layout.row(align=True)
if not tool.Ifc.get(): if not tool.Ifc.get():
@@ -159,12 +159,11 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
return operator.description or "" return operator.description or ""
def _execute(self, context): def _execute(self, context):
# self.props = context.scene.BIMCoveringProperties self.props = tool.Covering.get_covering_props()
getattr(self, f"hotkey_{self.hotkey}")() getattr(self, f"hotkey_{self.hotkey}")()
def invoke(self, context, event): def invoke(self, context, event):
# https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey # https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
# self.props = context.scene.BIMSpatialProperties
return self.execute(context) return self.execute(context)
def draw(self, context): def draw(self, context):
@@ -181,12 +181,12 @@ class RunAnalysis(bpy.types.Operator):
if modifier.type == "TRIANGULATE": if modifier.type == "TRIANGULATE":
return True return True
def get_covetool_category(self, obj): def get_covetool_category(self, obj: bpy.types.Object):
if not hasattr(obj, "data") or not isinstance(obj.data, bpy.types.Mesh): if not hasattr(obj, "data") or not isinstance(obj.data, bpy.types.Mesh):
return return
if not obj.BIMObjectProperties.ifc_definition_id: if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
return return
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = self.file.by_id(ifc_id)
ifc_class = element.is_a() ifc_class = element.is_a()
if "IfcSlab" in ifc_class: if "IfcSlab" in ifc_class:
return "floors" return "floors"
@@ -382,7 +382,7 @@ class InspectFromObject(bpy.types.Operator):
def get_active_object_ifc_definition(cls, context: bpy.types.Context) -> Union[int, None]: def get_active_object_ifc_definition(cls, context: bpy.types.Context) -> Union[int, None]:
obj = context.active_object obj = context.active_object
assert obj assert obj
if ifc_id := obj.BIMObjectProperties.ifc_definition_id: if ifc_id := tool.Blender.get_ifc_definition_id(obj):
return ifc_id return ifc_id
if ( if (
(data := obj.data) (data := obj.data)
@@ -65,7 +65,7 @@ class VisualiseDiff(bpy.types.Operator):
obj.color = (0.0, 0.0, 0.7, 1.0) obj.color = (0.0, 0.0, 0.7, 1.0)
continue continue
if not obj.BIMObjectProperties.ifc_definition_id: if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
continue continue
ifc_file = "" ifc_file = ""
@@ -84,7 +84,7 @@ class VisualiseDiff(bpy.types.Operator):
element_file = ifc_file element_file = ifc_file
try: try:
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = element_file.by_id(ifc_id)
except: except:
continue continue
global_id = getattr(element, "GlobalId", None) global_id = getattr(element, "GlobalId", None)
@@ -253,7 +253,7 @@ class SelectDiffObjects(bpy.types.Operator):
obj.select_set(True) obj.select_set(True)
continue continue
if not obj.BIMObjectProperties.ifc_definition_id: if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
continue continue
ifc_file = "" ifc_file = ""
@@ -272,7 +272,7 @@ class SelectDiffObjects(bpy.types.Operator):
element_file = ifc_file element_file = ifc_file
try: try:
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = element_file.by_id(ifc_id)
except: except:
continue continue
global_id = getattr(element, "GlobalId", None) global_id = getattr(element, "GlobalId", None)
+6 -4
View File
@@ -90,18 +90,20 @@ class BIM_PT_object_documents(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id): if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
return False return False
return bool(context.active_object.BIMObjectProperties.ifc_definition_id) if not tool.Ifc.get_object_by_identifier(ifc_id):
return False
return True
def draw(self, context): def draw(self, context):
if not ObjectDocumentData.is_loaded: if not ObjectDocumentData.is_loaded:
ObjectDocumentData.load() ObjectDocumentData.load()
obj = context.active_object obj = context.active_object
self.oprops = obj.BIMObjectProperties self.oprops = tool.Blender.get_object_bim_props(obj)
self.props = tool.Document.get_document_props() self.props = tool.Document.get_document_props()
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
@@ -51,7 +51,7 @@ class Annotator:
curve.font = font curve.font = font
props = tool.Drawing.get_text_props(obj) props = tool.Drawing.get_text_props(obj)
props.font_size = "2.5" props.font_size = "2.5"
collection = bpy.context.scene.camera.BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(bpy.context.scene.camera).collection
collection.objects.link(obj) collection.objects.link(obj)
Annotator.resize_text(obj) Annotator.resize_text(obj)
return obj return obj
@@ -133,7 +133,7 @@ class Annotator:
co1, _, _, _ = Annotator.get_placeholder_coords(camera) co1, _, _, _ = Annotator.get_placeholder_coords(camera)
matrix_world = tool.Drawing.get_camera_matrix(camera) matrix_world = tool.Drawing.get_camera_matrix(camera)
matrix_world.translation = co1 matrix_world.translation = co1
collection = camera.BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(camera).collection
if object_type == "TEXT": if object_type == "TEXT":
obj = bpy.data.objects.new(object_type, None) obj = bpy.data.objects.new(object_type, None)
@@ -343,7 +343,7 @@ def get_active_drawing(
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
try: try:
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id)) camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
return camera.BIMObjectProperties.collection, camera return tool.Blender.get_object_bim_props(camera).collection, camera
except: except:
return None, None return None, None
@@ -246,7 +246,7 @@ class CreateDrawing(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.props = tool.Drawing.get_document_props() self.props = tool.Drawing.get_document_props()
active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id active_drawing_id = tool.Blender.get_ifc_definition_id(context.scene.camera)
if self.print_all: if self.print_all:
original_drawing_id = active_drawing_id original_drawing_id = active_drawing_id
drawings_to_print = [d.ifc_definition_id for d in self.props.drawings if d.is_selected and d.is_drawing] drawings_to_print = [d.ifc_definition_id for d in self.props.drawings if d.is_selected and d.is_drawing]
@@ -391,7 +391,7 @@ class CreateDrawing(bpy.types.Operator):
bpy.ops.render.render(write_still=True) bpy.ops.render.render(write_still=True)
else: else:
previous_visibility = {} previous_visibility = {}
for obj in self.camera.BIMObjectProperties.collection.objects: for obj in tool.Blender.get_object_bim_props(self.camera).collection.objects:
if bpy.context.view_layer.objects.get(obj.name): if bpy.context.view_layer.objects.get(obj.name):
previous_visibility[obj.name] = obj.hide_get() previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True) obj.hide_set(True)
@@ -2095,7 +2095,7 @@ class ResizeText(bpy.types.Operator):
# TODO: check undo redo # TODO: check undo redo
def execute(self, context): def execute(self, context):
for obj in context.scene.camera.BIMObjectProperties.collection.objects: for obj in tool.Blender.get_object_bim_props(context.scene.camera).collection.objects:
if isinstance(obj.data, bpy.types.TextCurve): if isinstance(obj.data, bpy.types.TextCurve):
annotation.Annotator.resize_text(obj) annotation.Annotator.resize_text(obj)
return {"FINISHED"} return {"FINISHED"}
+1 -1
View File
@@ -266,7 +266,7 @@ def update_titleblocks(self, context):
def update_should_draw_decorations(self, context: bpy.types.Context) -> None: def update_should_draw_decorations(self, context: bpy.types.Context) -> None:
if self.should_draw_decorations: if self.should_draw_decorations:
# TODO: design a proper text variable templating renderer # TODO: design a proper text variable templating renderer
collection = context.scene.camera.BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(context.scene.camera).collection
for obj in collection.objects: for obj in collection.objects:
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]): if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
@@ -102,7 +102,7 @@ def block_scale(scene: bpy.types.Scene) -> None:
import bonsai.tool as tool import bonsai.tool as tool
if obj := (getattr(bpy.context, "active_object", None) or bpy.context.view_layer.objects.active): if obj := (getattr(bpy.context, "active_object", None) or bpy.context.view_layer.objects.active):
if isinstance(obj, bpy.types.Object) and obj.BIMObjectProperties.ifc_definition_id: if isinstance(obj, bpy.types.Object) and tool.Blender.get_ifc_definition_id(obj):
if obj.scale != (1, 1, 1): if obj.scale != (1, 1, 1):
obj.scale = (1, 1, 1) obj.scale = (1, 1, 1)
elif isinstance(obj, bpy.types.Mesh) and tool.Geometry.get_mesh_props(obj).ifc_definition_id: elif isinstance(obj, bpy.types.Mesh) and tool.Geometry.get_mesh_props(obj).ifc_definition_id:
@@ -464,7 +464,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
assert tool.Geometry.is_data_supported_for_adding_representation(data) assert tool.Geometry.is_data_supported_for_adding_representation(data)
mprops = tool.Geometry.get_mesh_props(data) mprops = tool.Geometry.get_mesh_props(data)
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) product = tool.Ifc.get_entity(obj)
assert product
material = ifcopenshell.util.element.get_material(product, should_skip_usage=True) material = ifcopenshell.util.element.get_material(product, should_skip_usage=True)
# NOTE: Currently iterator doesn't detect whether opening is actually affected the representation # NOTE: Currently iterator doesn't detect whether opening is actually affected the representation
@@ -903,7 +904,7 @@ class OverrideOutlinerDelete(bpy.types.Operator):
if element := tool.Ifc.get_entity(obj): if element := tool.Ifc.get_entity(obj):
if tool.Geometry.is_locked(element): if tool.Geometry.is_locked(element):
self.report({"ERROR"}, lock_error_message(obj.name)) self.report({"ERROR"}, lock_error_message(obj.name))
if collection := obj.BIMObjectProperties.collection: if collection := tool.Blender.get_object_bim_props(obj).collection:
collections_to_delete.discard(collection) collections_to_delete.discard(collection)
continue continue
tool.Geometry.delete_ifc_object(obj) tool.Geometry.delete_ifc_object(obj)
@@ -1067,7 +1068,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
continue continue
# clear object's collection so it will be able to have it's own # clear object's collection so it will be able to have it's own
new_obj.BIMObjectProperties.collection = None tool.Blender.get_object_bim_props(new_obj).collection = None
# copy the actual class # copy the actual class
new = bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj) new = bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
+10 -7
View File
@@ -334,9 +334,9 @@ class BIM_PT_connections(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(tool.Blender.get_ifc_definition_id(obj)):
return False return False
return tool.Ifc.get() return tool.Ifc.get()
@@ -345,7 +345,6 @@ class BIM_PT_connections(Panel):
ConnectionsData.load() ConnectionsData.load()
layout = self.layout layout = self.layout
props = context.active_object.BIMObjectProperties
if not ConnectionsData.data["connections"] and not ConnectionsData.data["is_connection_realization"]: if not ConnectionsData.data["connections"] and not ConnectionsData.data["is_connection_realization"]:
layout.label(text="No connections found") layout.label(text="No connections found")
@@ -480,12 +479,16 @@ class BIM_PT_placement(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return (obj := context.active_object) and obj.BIMObjectProperties.ifc_definition_id return (obj := context.active_object) and tool.Blender.get_ifc_definition_id(obj)
def draw(self, context): def draw(self, context):
if not PlacementData.is_loaded: if not PlacementData.is_loaded:
PlacementData.load() PlacementData.load()
obj = context.active_object
assert obj
props = tool.Blender.get_object_bim_props(obj)
if not PlacementData.data["has_placement"]: if not PlacementData.data["has_placement"]:
row = self.layout.row() row = self.layout.row()
row.label(text="No Object Placement Found") row.label(text="No Object Placement Found")
@@ -496,12 +499,12 @@ class BIM_PT_placement(Panel):
row = self.layout.row() row = self.layout.row()
row.prop(context.active_object, "rotation_euler", text="Rotation") row.prop(context.active_object, "rotation_euler", text="Rotation")
if context.active_object.BIMObjectProperties.blender_offset_type != "NONE": if props.blender_offset_type != "NONE":
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS") row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
row.label(text=context.active_object.BIMObjectProperties.blender_offset_type) row.label(text=props.blender_offset_type)
if context.active_object.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE": if props.blender_offset_type != "NOT_APPLICABLE":
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=PlacementData.data["original_x"], icon="EMPTY_AXIS") row.label(text=PlacementData.data["original_x"], icon="EMPTY_AXIS")
row.label(text=PlacementData.data["original_y"]) row.label(text=PlacementData.data["original_y"])
+3 -2
View File
@@ -91,9 +91,10 @@ class BIM_PT_object_groups(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
return tool.Ifc.get() and context.active_object.BIMObjectProperties.ifc_definition_id props = tool.Blender.get_object_bim_props(obj)
return tool.Ifc.get() and props.ifc_definition_id
def draw(self, context): def draw(self, context):
if not ObjectGroupsData.is_loaded: if not ObjectGroupsData.is_loaded:
@@ -374,16 +374,17 @@ class ObjectLog(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
cls.poll_message_set("No Active Object") cls.poll_message_set("No Active Object")
elif not context.active_object.BIMObjectProperties.ifc_definition_id: elif not tool.Blender.get_ifc_definition_id(obj):
cls.poll_message_set("Active Object doesn't have an IFC definition") cls.poll_message_set("Active Object doesn't have an IFC definition")
else: else:
return True return True
def execute(self, context): def execute(self, context):
obj = context.active_object
step_id = context.active_object.BIMObjectProperties.ifc_definition_id assert obj
step_id = tool.Blender.get_ifc_definition_id(obj)
core.entity_log(tool.IfcGit, tool.Ifc, step_id, self) core.entity_log(tool.IfcGit, tool.Ifc, step_id, self)
return {"FINISHED"} return {"FINISHED"}
+9 -7
View File
@@ -137,14 +137,14 @@ class BIM_PT_object_material(Panel):
def poll(cls, context): def poll(cls, context):
if not tool.Blender.is_tab(context, "GEOMETRY"): if not tool.Blender.is_tab(context, "GEOMETRY"):
return False return False
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
if not hasattr(tool.Ifc.get().by_id(props.ifc_definition_id), "HasAssociations"): if not hasattr(tool.Ifc.get().by_id(ifc_id), "HasAssociations"):
return False return False
return True return True
@@ -152,9 +152,11 @@ class BIM_PT_object_material(Panel):
if not ObjectMaterialData.is_loaded: if not ObjectMaterialData.is_loaded:
ObjectMaterialData.load() ObjectMaterialData.load()
obj = context.active_object
assert obj
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
self.oprops = context.active_object.BIMObjectProperties self.oprops = tool.Blender.get_object_bim_props(obj)
self.props = context.active_object.BIMObjectMaterialProperties self.props = obj.BIMObjectMaterialProperties
self.mprops = tool.Material.get_material_props() self.mprops = tool.Material.get_material_props()
if not ObjectMaterialData.data["materials"]: if not ObjectMaterialData.data["materials"]:
@@ -257,7 +257,7 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
sources = [] sources = []
for obj in bpy.context.selected_objects: for obj in bpy.context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id: if not tool.Blender.get_ifc_definition_id(obj):
continue continue
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
+13 -7
View File
@@ -72,7 +72,9 @@ class DisableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
context.active_object.BIMArrayProperties.is_editing = -1 obj = context.active_object
assert obj
tool.Model.get_array_props(obj).is_editing = -1
return {"FINISHED"} return {"FINISHED"}
@@ -84,8 +86,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMArrayProperties props = tool.Model.get_array_props(obj)
relating_obj = props.relating_array_object relating_obj = props.relating_array_object
@@ -119,7 +122,7 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMArrayProperties props = tool.Model.get_array_props(obj)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array") pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
@@ -182,7 +185,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMArrayProperties props = tool.Model.get_array_props(obj)
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array") pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
data = json.loads(pset["Data"]) data = json.loads(pset["Data"])
@@ -302,7 +305,8 @@ class Input3DCursorXArray(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMArrayProperties assert obj
props = tool.Model.get_array_props(obj)
cursor = context.scene.cursor cursor = context.scene.cursor
if props.use_local_space: if props.use_local_space:
props.x = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).x props.x = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).x
@@ -318,7 +322,8 @@ class Input3DCursorYArray(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMArrayProperties assert obj
props = tool.Model.get_array_props(obj)
cursor = context.scene.cursor cursor = context.scene.cursor
if props.use_local_space: if props.use_local_space:
props.y = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).y props.y = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).y
@@ -334,7 +339,8 @@ class Input3DCursorZArray(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMArrayProperties assert obj
props = tool.Model.get_array_props(obj)
cursor = context.scene.cursor cursor = context.scene.cursor
if props.use_local_space: if props.use_local_space:
props.z = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).z props.z = (Matrix.inverted(obj.matrix_world) @ cursor.matrix.translation).z
+18 -6
View File
@@ -385,7 +385,9 @@ class StairData:
@classmethod @classmethod
def general_params(cls): def general_params(cls):
props = bpy.context.active_object.BIMStairProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_stair_props(obj)
data = cls.data["pset_data"]["data_dict"] data = cls.data["pset_data"]["data_dict"]
general_params = {} general_params = {}
general_props = props.get_props_kwargs(stair_type=data["stair_type"]) general_props = props.get_props_kwargs(stair_type=data["stair_type"])
@@ -451,7 +453,9 @@ class WindowData:
@classmethod @classmethod
def general_params(cls): def general_params(cls):
props = bpy.context.active_object.BIMWindowProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_window_props(obj)
data = cls.data["pset_data"]["data_dict"] data = cls.data["pset_data"]["data_dict"]
general_params = {} general_params = {}
general_props = props.get_general_kwargs() general_props = props.get_general_kwargs()
@@ -462,7 +466,9 @@ class WindowData:
@classmethod @classmethod
def lining_params(cls): def lining_params(cls):
props = bpy.context.active_object.BIMWindowProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_window_props(obj)
data = cls.data["pset_data"]["data_dict"] data = cls.data["pset_data"]["data_dict"]
lining_data = data["lining_properties"] lining_data = data["lining_properties"]
lining_params = {} lining_params = {}
@@ -474,7 +480,9 @@ class WindowData:
@classmethod @classmethod
def panel_params(cls): def panel_params(cls):
props = bpy.context.active_object.BIMWindowProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_window_props(obj)
panel_data = cls.data["pset_data"]["data_dict"]["panel_properties"] panel_data = cls.data["pset_data"]["data_dict"]["panel_properties"]
panel_params = {} panel_params = {}
panel_props = props.get_panel_kwargs() panel_props = props.get_panel_kwargs()
@@ -565,7 +573,9 @@ class RailingData:
@classmethod @classmethod
def general_params(cls): def general_params(cls):
props = bpy.context.active_object.BIMRailingProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_railing_props(obj)
data = cls.data["pset_data"]["data_dict"] data = cls.data["pset_data"]["data_dict"]
general_params = {} general_params = {}
general_props = props.get_general_kwargs(railing_type=data["railing_type"]) general_props = props.get_general_kwargs(railing_type=data["railing_type"])
@@ -599,7 +609,9 @@ class RoofData:
@classmethod @classmethod
def general_params(cls): def general_params(cls):
props = bpy.context.active_object.BIMRoofProperties obj = bpy.context.active_object
assert obj
props = tool.Model.get_roof_props(obj)
data = cls.data["pset_data"]["data_dict"] data = cls.data["pset_data"]["data_dict"]
general_params = {} general_params = {}
general_props = props.get_general_kwargs(generation_method=data["generation_method"]) general_props = props.get_general_kwargs(generation_method=data["generation_method"])
@@ -718,14 +718,15 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator):
obj.matrix_world = newmat obj.matrix_world = newmat
def generate_box(usecase_path, ifc_file, settings): def generate_box(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None:
box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW") box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
if not box_context: if not box_context:
return return
obj = settings["blender_object"] obj = settings["blender_object"]
if 0 in list(obj.dimensions): if 0 in list(obj.dimensions):
return return
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id) product = tool.Ifc.get_entity(obj)
assert product
old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW") old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW")
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body": if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
if old_box: if old_box:
+145 -62
View File
@@ -28,34 +28,36 @@ from math import pi, radians
from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator from bonsai.bim.module.model.decorator import WallAxisDecorator, SlabDirectionDecorator
from bonsai.bim.module.model.door import update_door_modifier_bmesh from bonsai.bim.module.model.door import update_door_modifier_bmesh
from bonsai.bim.module.model.window import update_window_modifier_bmesh from bonsai.bim.module.model.window import update_window_modifier_bmesh
from typing import TYPE_CHECKING, Literal, get_args from typing import TYPE_CHECKING, Literal, get_args, Union, get_args
def get_ifc_class(self, context): def get_ifc_class(self: "BIMModelProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
if not AuthoringData.is_loaded: if not AuthoringData.is_loaded:
AuthoringData.load() AuthoringData.load()
return AuthoringData.data["ifc_classes"] return AuthoringData.data["ifc_classes"]
def get_boundary_class(self, context): def get_boundary_class(self: "BIMModelProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
if not AuthoringData.is_loaded: if not AuthoringData.is_loaded:
AuthoringData.load() AuthoringData.load()
return AuthoringData.data["boundary_class"] return AuthoringData.data["boundary_class"]
def get_relating_type_id(self, context): def get_relating_type_id(self: "BIMModelProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
if not AuthoringData.is_loaded: if not AuthoringData.is_loaded:
AuthoringData.load() AuthoringData.load()
return AuthoringData.data["relating_type_id"] return AuthoringData.data["relating_type_id"]
def get_materials(self, context): def get_materials(
self: Union["BIMWindowProperties", "BIMDoorProperties"], context: bpy.types.Context
) -> list[tuple[str, str, str]]:
if not AuthoringData.is_loaded: if not AuthoringData.is_loaded:
AuthoringData.load() AuthoringData.load()
return AuthoringData.data["materials"] return AuthoringData.data["materials"]
def update_ifc_class(self, context): def update_ifc_class(self: "BIMModelProperties", context: bpy.types.Context) -> None:
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class) bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
AuthoringData.data["ifc_class_current"] = self.ifc_class AuthoringData.data["ifc_class_current"] = self.ifc_class
AuthoringData.data["type_elements"] = AuthoringData.type_elements() AuthoringData.data["type_elements"] = AuthoringData.type_elements()
@@ -72,46 +74,46 @@ def update_ifc_class(self, context):
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types() AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
def update_relating_type_id(self, context): def update_relating_type_id(self: "BIMModelProperties", context: bpy.types.Context) -> None:
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id() AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
AuthoringData.data["relating_type_data"] = AuthoringData.relating_type_data() AuthoringData.data["relating_type_data"] = AuthoringData.relating_type_data()
self.type_page = [e[0] for e in AuthoringData.data["relating_type_id"]].index(self.relating_type_id) // 9 + 1 self.type_page = [e[0] for e in AuthoringData.data["relating_type_id"]].index(self.relating_type_id) // 9 + 1
def update_type_page(self, context): def update_type_page(self: "BIMModelProperties", context: bpy.types.Context) -> None:
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types() AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class, offset=9 * (self.type_page - 1), limit=9) bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class, offset=9 * (self.type_page - 1), limit=9)
self["type_page"] = min(self["type_page"], AuthoringData.data["total_pages"]) self["type_page"] = min(self["type_page"], AuthoringData.data["total_pages"])
self["type_page"] = max(self["type_page"], 1) self["type_page"] = max(self["type_page"], 1)
def update_relating_array_from_object(self, context): def update_relating_array_from_object(self: "BIMArrayProperties", context: bpy.types.Context) -> None:
bpy.ops.bim.enable_editing_array(item=self.is_editing) bpy.ops.bim.enable_editing_array(item=self.is_editing)
return return
def is_object_array_applicable(self, obj): def is_object_array_applicable(self: "BIMArrayProperties", obj: bpy.types.Object) -> bool:
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element: if not element:
return False return False
return ifcopenshell.util.element.get_pset(element, "BBIM_Array") return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
def update_wall_axis_decorator(self, context): def update_wall_axis_decorator(self: "BIMModelProperties", context: bpy.types.Context) -> None:
if self.show_wall_axis: if self.show_wall_axis:
WallAxisDecorator.install(bpy.context) WallAxisDecorator.install(bpy.context)
else: else:
WallAxisDecorator.uninstall() WallAxisDecorator.uninstall()
def update_slab_direction_decorator(self, context): def update_slab_direction_decorator(self: "BIMModelProperties", context: bpy.types.Context) -> None:
if self.show_slab_direction: if self.show_slab_direction:
SlabDirectionDecorator.install(bpy.context) SlabDirectionDecorator.install(bpy.context)
else: else:
SlabDirectionDecorator.uninstall() SlabDirectionDecorator.uninstall()
def update_search_name(self, context): def update_search_name(self: "BIMModelProperties", context: bpy.types.Context) -> None:
AuthoringData.load() AuthoringData.load()
# Total number of pages may decrease when using the search bar : # Total number of pages may decrease when using the search bar :
if self.type_page > AuthoringData.data["total_pages"]: if self.type_page > AuthoringData.data["total_pages"]:
@@ -119,17 +121,17 @@ def update_search_name(self, context):
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class) bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
def update_x_angle(self, context): def update_x_angle(self: "BIMModelProperties", context: bpy.types.Context) -> None:
angle_deg = math.degrees(self.x_angle) angle_deg = math.degrees(self.x_angle)
if tool.Cad.is_x(angle_deg, -90, 0.5) or tool.Cad.is_x(angle_deg, 90, 0.5): if tool.Cad.is_x(angle_deg, -90, 0.5) or tool.Cad.is_x(angle_deg, 90, 0.5):
self.x_angle = 0 self.x_angle = 0
def update_door(self, context): def update_door(self: "BIMDoorProperties", context: bpy.types.Context) -> None:
update_door_modifier_bmesh(context) update_door_modifier_bmesh(context)
def update_window(self, context): def update_window(self: "BIMWindowProperties", context: bpy.types.Context) -> None:
update_window_modifier_bmesh(context) update_window_modifier_bmesh(context)
@@ -303,36 +305,45 @@ class BIMArrayProperties(PropertyGroup):
poll=is_object_array_applicable, poll=is_object_array_applicable,
) )
if TYPE_CHECKING:
is_editing: int
count: int
x: float
y: float
z: float
use_local_space: bool
method: Literal["OFFSET", "DISTRIBUTE"]
sync_children: bool
relating_array_object: Union[bpy.types.Object, None]
def update_total_length_target(self, context):
def update_total_length_target(self: "BIMStairProperties", context: bpy.types.Context) -> None:
self["tread_run"] = self.total_length_target / (self.number_of_treads + 1) self["tread_run"] = self.total_length_target / (self.number_of_treads + 1)
def update_tread_run(self, context): def update_tread_run(self: "BIMStairProperties", context: bpy.types.Context) -> None:
if self.total_length_lock: if self.total_length_lock:
self["number_of_treads"] = int((self.total_length_target / self.tread_run) - 1) self["number_of_treads"] = int((self.total_length_target / self.tread_run) - 1)
else: else:
self["total_length_target"] = (self.number_of_treads + 1) * self.tread_run self["total_length_target"] = (self.number_of_treads + 1) * self.tread_run
def update_number_of_treads(self, context): def update_number_of_treads(self: "BIMStairProperties", context: bpy.types.Context) -> None:
if self.total_length_lock: if self.total_length_lock:
self["tread_run"] = self.total_length_target / (self.number_of_treads + 1) self["tread_run"] = self.total_length_target / (self.number_of_treads + 1)
else: else:
self["total_length_target"] = (self.number_of_treads + 1) * self.tread_run self["total_length_target"] = (self.number_of_treads + 1) * self.tread_run
StairType = Literal["CONCRETE", "WOOD/STEEL", "GENERIC"]
class BIMStairProperties(PropertyGroup): class BIMStairProperties(PropertyGroup):
def validate_nosing_value(self, context): def validate_nosing_value(self, context: bpy.types.Context) -> None:
if self.stair_type != "WOOD/STEEL" and self.nosing_length < 0: if self.stair_type != "WOOD/STEEL" and self.nosing_length < 0:
self["nosing_length"] = 0 self["nosing_length"] = 0
non_si_units_props = ("is_editing", "number_of_treads", "has_top_nib", "stair_type") non_si_units_props = ("is_editing", "number_of_treads", "has_top_nib", "stair_type")
stair_types = (
("CONCRETE", "Concrete", ""),
("WOOD/STEEL", "Wood / Steel", ""),
("GENERIC", "Generic", ""),
)
is_editing: bpy.props.BoolProperty(default=False) is_editing: bpy.props.BoolProperty(default=False)
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01, subtype="DISTANCE") width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01, subtype="DISTANCE")
@@ -361,7 +372,10 @@ class BIMStairProperties(PropertyGroup):
top_slab_depth: bpy.props.FloatProperty(name="Top Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE") top_slab_depth: bpy.props.FloatProperty(name="Top Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE")
has_top_nib: bpy.props.BoolProperty(name="Has Top Nib", default=True) has_top_nib: bpy.props.BoolProperty(name="Has Top Nib", default=True)
stair_type: bpy.props.EnumProperty( stair_type: bpy.props.EnumProperty(
name="Stair Type", items=stair_types, default="CONCRETE", update=validate_nosing_value name="Stair Type",
items=[(i, i.replace("/", " / ").title(), "") for i in get_args(StairType)],
default="CONCRETE",
update=validate_nosing_value,
) )
custom_first_last_tread_run: bpy.props.FloatVectorProperty( custom_first_last_tread_run: bpy.props.FloatVectorProperty(
name="Custom First / Last Treads Widths", name="Custom First / Last Treads Widths",
@@ -385,6 +399,23 @@ class BIMStairProperties(PropertyGroup):
name="Nosing Depth", description="Depth of the tread's nosing", min=0, default=0, unit="LENGTH" name="Nosing Depth", description="Depth of the tread's nosing", min=0, default=0, unit="LENGTH"
) )
if TYPE_CHECKING:
is_editing: bool
width: float
height: float
number_of_treads: int
total_length_target: float
total_length_lock: bool
tread_depth: float
tread_run: float
base_slab_depth: float
top_slab_depth: float
has_top_nib: bool
stair_type: str
custom_first_last_tread_run: tuple[float, float]
nosing_length: float
nosing_depth: float
def get_props_kwargs(self, convert_to_project_units=False, stair_type=None): def get_props_kwargs(self, convert_to_project_units=False, stair_type=None):
if not stair_type: if not stair_type:
stair_type = self.stair_type stair_type = self.stair_type
@@ -445,20 +476,22 @@ def window_type_prop_update(self, context):
update_window(self, context) update_window(self, context)
WindowType = Literal[
"SINGLE_PANEL",
"DOUBLE_PANEL_HORIZONTAL",
"DOUBLE_PANEL_VERTICAL",
"TRIPLE_PANEL_BOTTOM",
"TRIPLE_PANEL_TOP",
"TRIPLE_PANEL_LEFT",
"TRIPLE_PANEL_RIGHT",
"TRIPLE_PANEL_HORIZONTAL",
"TRIPLE_PANEL_VERTICAL",
]
# default prop values are in mm and converted later # default prop values are in mm and converted later
class BIMWindowProperties(PropertyGroup): class BIMWindowProperties(PropertyGroup):
non_si_units_props = ("is_editing", "window_type") non_si_units_props = ("is_editing", "window_type")
window_types = (
("SINGLE_PANEL", "SINGLE_PANEL", ""),
("DOUBLE_PANEL_HORIZONTAL", "DOUBLE_PANEL_HORIZONTAL", ""),
("DOUBLE_PANEL_VERTICAL", "DOUBLE_PANEL_VERTICAL", ""),
("TRIPLE_PANEL_BOTTOM", "TRIPLE_PANEL_BOTTOM", ""),
("TRIPLE_PANEL_TOP", "TRIPLE_PANEL_TOP", ""),
("TRIPLE_PANEL_LEFT", "TRIPLE_PANEL_LEFT", ""),
("TRIPLE_PANEL_RIGHT", "TRIPLE_PANEL_RIGHT", ""),
("TRIPLE_PANEL_HORIZONTAL", "TRIPLE_PANEL_HORIZONTAL", ""),
("TRIPLE_PANEL_VERTICAL", "TRIPLE_PANEL_VERTICAL", ""),
)
# number of panels and default mullion/transom values # number of panels and default mullion/transom values
# fmt: off # fmt: off
@@ -477,7 +510,10 @@ class BIMWindowProperties(PropertyGroup):
is_editing: bpy.props.BoolProperty(default=False) is_editing: bpy.props.BoolProperty(default=False)
window_type: bpy.props.EnumProperty( window_type: bpy.props.EnumProperty(
name="Window Type", items=window_types, default="SINGLE_PANEL", update=window_type_prop_update name="Window Type",
items=[(i, i, "") for i in get_args(WindowType)],
default="SINGLE_PANEL",
update=window_type_prop_update,
) )
overall_height: bpy.props.FloatProperty( overall_height: bpy.props.FloatProperty(
name="Overall Height", default=0.9, subtype="DISTANCE", update=update_window name="Overall Height", default=0.9, subtype="DISTANCE", update=update_window
@@ -546,6 +582,31 @@ class BIMWindowProperties(PropertyGroup):
framing_material: bpy.props.EnumProperty(name="Framing Material", items=get_materials, options=set()) framing_material: bpy.props.EnumProperty(name="Framing Material", items=get_materials, options=set())
glazing_material: bpy.props.EnumProperty(name="Glazing Material", items=get_materials, options=set()) glazing_material: bpy.props.EnumProperty(name="Glazing Material", items=get_materials, options=set())
if TYPE_CHECKING:
is_editing: bool
window_type: WindowType
overall_height: float
overall_width: float
lining_depth: float
lining_thickness: float
lining_offset: float
lining_to_panel_offset_x: float
lining_to_panel_offset_y: float
mullion_thickness: float
first_mullion_offset: float
second_mullion_offset: float
first_transom_offset: float
second_transom_offset: float
# Panel properties.
frame_depth: tuple[float, float, float]
frame_thickness: tuple[float, float, float]
# Material properties.
lining_material: str
framing_material: str
glazing_material: str
def get_general_kwargs(self, convert_to_project_units=False): def get_general_kwargs(self, convert_to_project_units=False):
kwargs = { kwargs = {
"window_type": self.window_type, "window_type": self.window_type,
@@ -824,6 +885,10 @@ class BIMDoorProperties(PropertyGroup):
setattr(self, prop_name, kwargs[prop_name]) setattr(self, prop_name, kwargs[prop_name])
RailingType = Literal["FRAMELESS_PANEL", "WALL_MOUNTED_HANDRAIL"]
CapType = Literal["TO_END_POST_AND_FLOOR", "TO_END_POST", "TO_FLOOR", "TO_WALL", "180", "NONE"]
class BIMRailingProperties(PropertyGroup): class BIMRailingProperties(PropertyGroup):
non_si_units_props = ( non_si_units_props = (
"is_editing", "is_editing",
@@ -833,23 +898,12 @@ class BIMRailingProperties(PropertyGroup):
"path_data", "path_data",
) )
railing_types = (
("FRAMELESS_PANEL", "FRAMELESS_PANEL", ""),
("WALL_MOUNTED_HANDRAIL", "WALL_MOUNTED_HANDRAIL", ""),
)
cap_types = (
("TO_END_POST_AND_FLOOR", "TO_END_POST_AND_FLOOR", ""),
("TO_END_POST", "TO_END_POST", ""),
("TO_FLOOR", "TO_FLOOR", ""),
("TO_WALL", "TO_WALL", ""),
("180", "180", ""),
("NONE", "NONE", ""),
)
is_editing: bpy.props.BoolProperty(default=False) is_editing: bpy.props.BoolProperty(default=False)
is_editing_path: bpy.props.BoolProperty(default=False) is_editing_path: bpy.props.BoolProperty(default=False)
railing_type: bpy.props.EnumProperty(name="Railing Type", items=railing_types, default="FRAMELESS_PANEL") railing_type: bpy.props.EnumProperty(
name="Railing Type", items=[(i, i, "") for i in get_args(RailingType)], default="FRAMELESS_PANEL"
)
height: bpy.props.FloatProperty(name="Height", default=1.0, subtype="DISTANCE") height: bpy.props.FloatProperty(name="Height", default=1.0, subtype="DISTANCE")
thickness: bpy.props.FloatProperty(name="Thickness", default=0.050, subtype="DISTANCE") thickness: bpy.props.FloatProperty(name="Thickness", default=0.050, subtype="DISTANCE")
spacing: bpy.props.FloatProperty(name="Spacing", default=0.050, subtype="DISTANCE") spacing: bpy.props.FloatProperty(name="Spacing", default=0.050, subtype="DISTANCE")
@@ -875,7 +929,24 @@ class BIMRailingProperties(PropertyGroup):
description="Clear width between the railing and the wall", description="Clear width between the railing and the wall",
subtype="DISTANCE", subtype="DISTANCE",
) )
terminal_type: bpy.props.EnumProperty(name="Terminal Type", items=cap_types, default="180") terminal_type: bpy.props.EnumProperty(
name="Terminal Type", items=[(i, i, "") for i in get_args(CapType)], default="180"
)
if TYPE_CHECKING:
is_editing: bool
is_editing_path: bool
railing_type: RailingType
height: float
thickness: float
spacing: float
use_manual_supports: bool
support_spacing: float
railing_diameter: float
clear_width: float
terminal_type: CapType
def get_general_kwargs(self, railing_type=None, convert_to_project_units=False): def get_general_kwargs(self, railing_type=None, convert_to_project_units=False):
if railing_type is None: if railing_type is None:
@@ -921,11 +992,15 @@ def to_percentage(angle: float) -> float:
return math.tan(angle) * 100 return math.tan(angle) * 100
RoofType = Literal["HIP/GABLE ROOF"]
RoofGenerationMethod = Literal["HEIGHT", "ANGLE"]
class BIMRoofProperties(PropertyGroup): class BIMRoofProperties(PropertyGroup):
def update_angle(self, context) -> None: def update_angle(self, context: bpy.types.Context) -> None:
self["angle"] = to_angle(self.percentage) self["angle"] = to_angle(self.percentage)
def update_percentage(self, context) -> None: def update_percentage(self, context: bpy.types.Context) -> None:
self["percentage"] = to_percentage(self.angle) self["percentage"] = to_percentage(self.angle)
non_si_units_props = ( non_si_units_props = (
@@ -937,18 +1012,15 @@ class BIMRoofProperties(PropertyGroup):
"percentage", "percentage",
"rafter_edge_angle", "rafter_edge_angle",
) )
roof_types = (("HIP/GABLE ROOF", "HIP/GABLE ROOF", ""),)
roof_generation_methods = (
("HEIGHT", "HEIGHT", ""),
("ANGLE", "ANGLE", ""),
)
is_editing: bpy.props.BoolProperty(default=False) is_editing: bpy.props.BoolProperty(default=False)
is_editing_path: bpy.props.BoolProperty(default=False) is_editing_path: bpy.props.BoolProperty(default=False)
roof_type: bpy.props.EnumProperty(name="Roof Type", items=roof_types, default="HIP/GABLE ROOF") roof_type: bpy.props.EnumProperty(
name="Roof Type", items=[(i, i, "") for i in get_args(RoofType)], default="HIP/GABLE ROOF"
)
generation_method: bpy.props.EnumProperty( generation_method: bpy.props.EnumProperty(
name="Roof Generation Method", items=roof_generation_methods, default="ANGLE" name="Roof Generation Method", items=[(i, i, "") for i in get_args(RoofGenerationMethod)], default="ANGLE"
) )
height: bpy.props.FloatProperty( height: bpy.props.FloatProperty(
name="Height", default=1.0, description="Maximum height of the roof to be generated.", subtype="DISTANCE" name="Height", default=1.0, description="Maximum height of the roof to be generated.", subtype="DISTANCE"
@@ -978,6 +1050,17 @@ class BIMRoofProperties(PropertyGroup):
name="Rafter Edge Angle", min=0, max=pi / 2, default=pi / 2, subtype="ANGLE" name="Rafter Edge Angle", min=0, max=pi / 2, default=pi / 2, subtype="ANGLE"
) )
if TYPE_CHECKING:
is_editing: bool
is_editing_path: bool
roof_type: Literal["HIP/GABLE ROOF"]
generation_method: Literal["HEIGHT", "ANGLE"]
height: float
angle: float
percentage: float
roof_thickness: float
rafter_edge_angle: float
def get_general_kwargs(self, generation_method=None, convert_to_project_units=False): def get_general_kwargs(self, generation_method=None, convert_to_project_units=False):
if generation_method is None: if generation_method is None:
generation_method = self.generation_method generation_method = self.generation_method
+33 -13
View File
@@ -55,8 +55,10 @@ def update_railing_modifier_ifc_data(context: bpy.types.Context) -> None:
since it's going to update ifc representation since it's going to update ifc representation
""" """
obj = context.active_object obj = context.active_object
props = obj.BIMRailingProperties assert obj
props = tool.Model.get_railing_props(obj)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
# type attributes # type attributes
@@ -122,7 +124,8 @@ def update_railing_modifier_bmesh(context: bpy.types.Context) -> None:
If BBIM Pset just changed should call refresh() before updating bmesh If BBIM Pset just changed should call refresh() before updating bmesh
""" """
obj = context.active_object obj = context.active_object
props = obj.BIMRailingProperties assert obj
props = tool.Model.get_railing_props(obj)
V_ = tool.Blender.V_ V_ = tool.Blender.V_
# NOTE: using Data since bmesh update will hapen very often # NOTE: using Data since bmesh update will hapen very often
@@ -327,8 +330,10 @@ class AddRailing(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRailingProperties assert element
props = tool.Model.get_railing_props(obj)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
railing_data = props.get_general_kwargs(convert_to_project_units=True) railing_data = props.get_general_kwargs(convert_to_project_units=True)
@@ -367,7 +372,8 @@ class CopyRailingParameters(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
source_obj = context.active_object source_obj = context.active_object
source_props = source_obj.BIMRailingProperties assert source_obj
source_props = tool.Model.get_railing_props(source_obj)
railing_data = source_props.get_general_kwargs(convert_to_project_units=True) railing_data = source_props.get_general_kwargs(convert_to_project_units=True)
for target_obj in context.selected_objects: for target_obj in context.selected_objects:
@@ -379,7 +385,8 @@ class CopyRailingParameters(bpy.types.Operator, tool.Ifc.Operator):
continue continue
railing_data["path_data"] = RailingData.data["path_data"] railing_data["path_data"] = RailingData.data["path_data"]
target_element = tool.Ifc.get_entity(target_obj) target_element = tool.Ifc.get_entity(target_obj)
target_props = target_obj.BIMRailingProperties assert target_element
target_props = tool.Model.get_railing_props(target_obj)
target_props.set_props_kwargs_from_ifc_data(railing_data) target_props.set_props_kwargs_from_ifc_data(railing_data)
update_bbim_railing_pset(target_element, railing_data) update_bbim_railing_pset(target_element, railing_data)
@@ -398,7 +405,8 @@ class EnableEditingRailing(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMRailingProperties assert obj
props = tool.Model.get_railing_props(obj)
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"]
data["path_data"] = json.dumps(data["path_data"]) data["path_data"] = json.dumps(data["path_data"])
@@ -416,8 +424,9 @@ class CancelEditingRailing(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"]
props = obj.BIMRailingProperties props = tool.Model.get_railing_props(obj)
# restore previous settings since editing was canceled # restore previous settings since editing was canceled
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
@@ -434,8 +443,10 @@ class FinishEditingRailing(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRailingProperties assert element
props = tool.Model.get_railing_props(obj)
pset_data = tool.Model.get_modeling_bbim_pset_data(bpy.context.active_object, "BBIM_Railing") pset_data = tool.Model.get_modeling_bbim_pset_data(bpy.context.active_object, "BBIM_Railing")
path_data = pset_data["data_dict"]["path_data"] path_data = pset_data["data_dict"]["path_data"]
@@ -457,8 +468,10 @@ class FlipRailingPathOrder(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRailingProperties assert element
props = tool.Model.get_railing_props(obj)
pset_data = tool.Model.get_modeling_bbim_pset_data(bpy.context.active_object, "BBIM_Railing") pset_data = tool.Model.get_modeling_bbim_pset_data(bpy.context.active_object, "BBIM_Railing")
path_data = pset_data["data_dict"]["path_data"] path_data = pset_data["data_dict"]["path_data"]
@@ -488,7 +501,8 @@ class EnableEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
[o.select_set(False) for o in context.selected_objects if o != obj] [o.select_set(False) for o in context.selected_objects if o != obj]
props = obj.BIMRailingProperties assert obj
props = tool.Model.get_railing_props(obj)
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Railing")["data_dict"]
# required since we could load pset from .ifc and BIMRoofProperties won't be set # required since we could load pset from .ifc and BIMRoofProperties won't be set
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
@@ -505,7 +519,8 @@ class EnableEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
def cancel_editing_railing_path(context: bpy.types.Context) -> set[str]: def cancel_editing_railing_path(context: bpy.types.Context) -> set[str]:
obj = context.active_object obj = context.active_object
props = obj.BIMRailingProperties assert obj
props = tool.Model.get_railing_props(obj)
ProfileDecorator.uninstall() ProfileDecorator.uninstall()
props.is_editing_path = False props.is_editing_path = False
@@ -517,6 +532,7 @@ def cancel_editing_railing_path(context: bpy.types.Context) -> set[str]:
update_railing_modifier_bmesh(context) update_railing_modifier_bmesh(context)
else: else:
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
bonsai.core.geometry.switch_representation( bonsai.core.geometry.switch_representation(
tool.Ifc, tool.Ifc,
@@ -547,8 +563,9 @@ class FinishEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRailingProperties props = tool.Model.get_railing_props(obj)
railing_data = props.get_general_kwargs(convert_to_project_units=True) railing_data = props.get_general_kwargs(convert_to_project_units=True)
path_data = get_path_data(obj) path_data = get_path_data(obj)
@@ -574,8 +591,11 @@ class RemoveRailing(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
obj.BIMRailingProperties.is_editing = False assert element
props = tool.Model.get_railing_props(obj)
props.is_editing = False
pset = tool.Pset.get_element_pset(element, "BBIM_Railing") pset = tool.Pset.get_element_pset(element, "BBIM_Railing")
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset) ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
+18 -12
View File
@@ -409,7 +409,8 @@ def update_roof_modifier_ifc_data(context: bpy.types.Context) -> None:
since it's going to update ifc representation since it's going to update ifc representation
""" """
obj = context.active_object obj = context.active_object
props = obj.BIMRoofProperties assert obj
props = tool.Model.get_roof_props(obj)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
def roof_is_gabled() -> bool: def roof_is_gabled() -> bool:
@@ -442,7 +443,7 @@ def update_roof_modifier_bmesh(obj: bpy.types.Object) -> None:
"""before using should make sure that Data contains up-to-date information. """before using should make sure that Data contains up-to-date information.
If BBIM Pset just changed should call refresh() before updating bmesh If BBIM Pset just changed should call refresh() before updating bmesh
""" """
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
assert isinstance(obj.data, bpy.types.Mesh) assert isinstance(obj.data, bpy.types.Mesh)
# NOTE: using Data since bmesh update will hapen very often # NOTE: using Data since bmesh update will hapen very often
@@ -559,7 +560,7 @@ class AddRoof(bpy.types.Operator, tool.Ifc.Operator):
obj = context.active_object obj = context.active_object
assert obj assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
# rejecting original roof shape to be safe # rejecting original roof shape to be safe
@@ -607,7 +608,8 @@ class EnableEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMRoofProperties assert obj
props = tool.Model.get_roof_props(obj)
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"]
# required since we could load pset from .ifc and BIMRoofProperties won't be set # required since we could load pset from .ifc and BIMRoofProperties won't be set
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
@@ -624,7 +626,7 @@ class CancelEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
obj = context.active_object obj = context.active_object
assert obj assert obj
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"]
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
# restore previous settings since editing was canceled # restore previous settings since editing was canceled
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
@@ -642,7 +644,7 @@ class FinishEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
pset_data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof") pset_data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")
path_data = pset_data["data_dict"]["path_data"] path_data = pset_data["data_dict"]["path_data"]
@@ -666,7 +668,7 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
obj = context.active_object obj = context.active_object
assert obj assert obj
[o.select_set(False) for o in context.selected_objects if o != obj] [o.select_set(False) for o in context.selected_objects if o != obj]
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"] data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")["data_dict"]
# required since we could load pset from .ifc and BIMRoofProperties won't be set # required since we could load pset from .ifc and BIMRoofProperties won't be set
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
@@ -720,7 +722,7 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
def cancel_editing_roof_path(context: bpy.types.Context) -> set[str]: def cancel_editing_roof_path(context: bpy.types.Context) -> set[str]:
obj = context.active_object obj = context.active_object
assert obj assert obj
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
ProfileDecorator.uninstall() ProfileDecorator.uninstall()
props.is_editing_path = False props.is_editing_path = False
@@ -751,7 +753,8 @@ class CopyRoofParameters(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
source_obj = context.active_object source_obj = context.active_object
source_props = source_obj.BIMRoofProperties assert source_obj
source_props = tool.Model.get_roof_props(source_obj)
data = source_props.get_general_kwargs(convert_to_project_units=True) data = source_props.get_general_kwargs(convert_to_project_units=True)
for target_obj in context.selected_objects: for target_obj in context.selected_objects:
@@ -763,7 +766,7 @@ class CopyRoofParameters(bpy.types.Operator, tool.Ifc.Operator):
continue continue
data["path_data"] = RoofData.data["path_data"] data["path_data"] = RoofData.data["path_data"]
target_element = tool.Ifc.get_entity(target_obj) target_element = tool.Ifc.get_entity(target_obj)
target_props = target_obj.BIMRoofProperties target_props = tool.Model.get_roof_props(target_obj)
target_props.set_props_kwargs_from_ifc_data(data) target_props.set_props_kwargs_from_ifc_data(data)
update_bbim_roof_pset(target_element, data) update_bbim_roof_pset(target_element, data)
@@ -783,7 +786,7 @@ class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
bm = tool.Blender.get_bmesh_for_mesh(obj.data) bm = tool.Blender.get_bmesh_for_mesh(obj.data)
op_status, error_message = is_valid_roof_footprint(bm) op_status, error_message = is_valid_roof_footprint(bm)
@@ -815,9 +818,12 @@ class RemoveRoof(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
obj.BIMRoofProperties.is_editing = False props = tool.Model.get_roof_props(obj)
props.is_editing = False
assert element
pset = tool.Pset.get_element_pset(element, "BBIM_Roof") pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset) ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
return {"FINISHED"} return {"FINISHED"}
+18 -8
View File
@@ -34,7 +34,8 @@ from bpy_extras.object_utils import AddObjectHelper, object_data_add
def regenerate_stair_mesh(obj: bpy.types.Object) -> None: def regenerate_stair_mesh(obj: bpy.types.Object) -> None:
props_kwargs = obj.BIMStairProperties.get_props_kwargs() props = tool.Model.get_stair_props(obj)
props_kwargs = props.get_props_kwargs()
vertices, edges, faces = tool.Model.generate_stair_2d_profile(**props_kwargs) vertices, edges, faces = tool.Model.generate_stair_2d_profile(**props_kwargs)
bm = bmesh.new() bm = bmesh.new()
@@ -69,7 +70,8 @@ def update_ifc_stair_props(obj: bpy.types.Object) -> None:
since it's going to update ifc representation since it's going to update ifc representation
""" """
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMStairProperties assert element
props = tool.Model.get_stair_props(obj)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
if tool.Ifc.get_schema() != "IFC2X3" and element.is_a("IfcStairFlight"): if tool.Ifc.get_schema() != "IFC2X3" and element.is_a("IfcStairFlight"):
@@ -183,7 +185,8 @@ class AddStair(bpy.types.Operator, tool.Ifc.Operator):
obj = context.active_object obj = context.active_object
assert obj assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMStairProperties assert element
props = tool.Model.get_stair_props(obj)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
stair_data = props.get_props_kwargs(convert_to_project_units=True) stair_data = props.get_props_kwargs(convert_to_project_units=True)
@@ -214,9 +217,11 @@ class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data")) data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data"))
props = obj.BIMStairProperties props = tool.Model.get_stair_props(obj)
# restore previous settings since editing was canceled # restore previous settings since editing was canceled
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
regenerate_stair_mesh(obj) regenerate_stair_mesh(obj)
@@ -233,8 +238,10 @@ class FinishEditingStair(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMStairProperties assert element
props = tool.Model.get_stair_props(obj)
data = props.get_props_kwargs(convert_to_project_units=True) data = props.get_props_kwargs(convert_to_project_units=True)
props.is_editing = False props.is_editing = False
@@ -257,7 +264,8 @@ class EnableEditingStair(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMStairProperties assert obj
props = tool.Model.get_stair_props(obj)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data")) data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Stair", "Data"))
# required since we could load pset from .ifc and BIMStairProperties won't be set # required since we could load pset from .ifc and BIMStairProperties won't be set
@@ -273,9 +281,11 @@ class RemoveStair(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMStairProperties assert obj
props = tool.Model.get_stair_props(obj)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
obj.BIMStairProperties.is_editing = False assert element
props.is_editing = False
pset = tool.Pset.get_element_pset(element, "BBIM_Stair") pset = tool.Pset.get_element_pset(element, "BBIM_Stair")
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset) ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
+11 -5
View File
@@ -217,7 +217,9 @@ class BIM_PT_array(bpy.types.Panel):
if not ArrayData.is_loaded: if not ArrayData.is_loaded:
ArrayData.load() ArrayData.load()
props = context.active_object.BIMArrayProperties obj = context.active_object
assert obj
props = tool.Model.get_array_props(obj)
if ArrayData.data["parameters"]: if ArrayData.data["parameters"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -291,7 +293,7 @@ class BIM_PT_stair(bpy.types.Panel):
obj = context.active_object obj = context.active_object
assert obj assert obj
props = obj.BIMStairProperties props = tool.Model.get_stair_props(obj)
if StairData.data["pset_data"]: if StairData.data["pset_data"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -404,7 +406,9 @@ class BIM_PT_window(bpy.types.Panel):
if not WindowData.is_loaded: if not WindowData.is_loaded:
WindowData.load() WindowData.load()
props = context.active_object.BIMWindowProperties obj = context.active_object
assert obj
props = tool.Model.get_window_props(obj)
if WindowData.data["pset_data"]: if WindowData.data["pset_data"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -594,7 +598,9 @@ class BIM_PT_railing(bpy.types.Panel):
if not RailingData.is_loaded: if not RailingData.is_loaded:
RailingData.load() RailingData.load()
props = context.active_object.BIMRailingProperties obj = context.active_object
assert obj
props = tool.Model.get_railing_props(obj)
if RailingData.data["pset_data"]: if RailingData.data["pset_data"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -659,7 +665,7 @@ class BIM_PT_roof(bpy.types.Panel):
obj = context.active_object obj = context.active_object
assert obj assert obj
props = obj.BIMRoofProperties props = tool.Model.get_roof_props(obj)
if RoofData.data["pset_data"]: if RoofData.data["pset_data"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
+21 -8
View File
@@ -41,8 +41,10 @@ V_ = tool.Blender.V_
def update_window_modifier_representation(context: bpy.types.Context) -> None: def update_window_modifier_representation(context: bpy.types.Context) -> None:
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMWindowProperties assert element
props = tool.Model.get_window_props(obj)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
@@ -258,7 +260,8 @@ def create_bm_window(
def update_window_modifier_bmesh(context: bpy.types.Context) -> None: def update_window_modifier_bmesh(context: bpy.types.Context) -> None:
obj = context.active_object obj = context.active_object
props = obj.BIMWindowProperties assert obj
props = tool.Model.get_window_props(obj)
panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type] panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type]
accumulated_height = [0] * len(panel_schema[0]) accumulated_height = [0] * len(panel_schema[0])
built_panels = [] built_panels = []
@@ -447,8 +450,10 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMWindowProperties assert element
props = tool.Model.get_window_props(obj)
window_data = props.get_general_kwargs(convert_to_project_units=True) window_data = props.get_general_kwargs(convert_to_project_units=True)
lining_props = props.get_lining_kwargs(convert_to_project_units=True) lining_props = props.get_lining_kwargs(convert_to_project_units=True)
@@ -477,11 +482,13 @@ class CancelEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data")) data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data"))
data.update(data.pop("lining_properties")) data.update(data.pop("lining_properties"))
data.update(data.pop("panel_properties")) data.update(data.pop("panel_properties"))
props = obj.BIMWindowProperties props = tool.Model.get_window_props(obj)
props.set_props_kwargs_from_ifc_data(data) props.set_props_kwargs_from_ifc_data(data)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
@@ -506,8 +513,10 @@ class FinishEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
props = obj.BIMWindowProperties assert element
props = tool.Model.get_window_props(obj)
window_data = props.get_general_kwargs(convert_to_project_units=True) window_data = props.get_general_kwargs(convert_to_project_units=True)
lining_props = props.get_lining_kwargs(convert_to_project_units=True) lining_props = props.get_lining_kwargs(convert_to_project_units=True)
@@ -533,8 +542,10 @@ class EnableEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMWindowProperties assert obj
props = tool.Model.get_window_props(obj)
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data")) data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data"))
data.update(data.pop("lining_properties")) data.update(data.pop("lining_properties"))
data.update(data.pop("panel_properties")) data.update(data.pop("panel_properties"))
@@ -553,9 +564,11 @@ class RemoveWindow(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
props = obj.BIMWindowProperties assert obj
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
obj.BIMWindowProperties.is_editing = False assert element
props = tool.Model.get_window_props(obj)
props.is_editing = False
pset = tool.Pset.get_element_pset(element, "BBIM_Window") pset = tool.Pset.get_element_pset(element, "BBIM_Window")
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset) ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
@@ -1046,6 +1046,7 @@ class EditObjectUI:
@classmethod @classmethod
def draw_modes(cls, context: bpy.types.Context) -> None: def draw_modes(cls, context: bpy.types.Context) -> None:
obj = context.active_object
ui_context = str(context.region.type) ui_context = str(context.region.type)
row = cls.layout.row(align=True) row = cls.layout.row(align=True)
row.separator() row.separator()
@@ -1055,9 +1056,7 @@ class EditObjectUI:
if len(context.selected_objects) == 1 and AuthoringData.data["has_extrusion"]: if len(context.selected_objects) == 1 and AuthoringData.data["has_extrusion"]:
row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row
add_layout_hotkey_operator(row, "Edit Profile", "S_E", "", ui_context) add_layout_hotkey_operator(row, "Edit Profile", "S_E", "", ui_context)
elif ( elif tool.Model.is_parametric_railing_active() and not tool.Model.get_railing_props(obj).is_editing_path:
tool.Model.is_parametric_railing_active() and not context.active_object.BIMRailingProperties.is_editing_path
):
row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row
row.operator( row.operator(
"bim.enable_editing_railing_path", "bim.enable_editing_railing_path",
@@ -1208,12 +1207,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
# and it might conflict with one of the conditions below # and it might conflict with one of the conditions below
if ( if (
tool.Model.is_parametric_railing_active() tool.Model.is_parametric_railing_active()
and not bpy.context.active_object.BIMRailingProperties.is_editing_path and not tool.Model.get_railing_props(active_object).is_editing_path
): ):
bpy.ops.bim.enable_editing_railing_path() bpy.ops.bim.enable_editing_railing_path()
return return
elif tool.Model.is_parametric_roof_active() and not bpy.context.active_object.BIMRoofProperties.is_editing_path: elif tool.Model.is_parametric_roof_active() and not tool.Model.get_roof_props(active_object).is_editing_path:
# undo the unselection done above because roof has no usage type # undo the unselection done above because roof has no usage type
bpy.ops.bim.enable_editing_roof_path() bpy.ops.bim.enable_editing_roof_path()
return return
+1 -1
View File
@@ -40,7 +40,7 @@ def update_relating_object(self, context):
if self.relating_object is None: if self.relating_object is None:
return return
if not self.relating_object.BIMObjectProperties.ifc_definition_id: if not tool.Blender.get_ifc_definition_id(self.relating_object):
context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO") context.window_manager.popup_menu(message, title="Invalid Element Selected", icon="INFO")
self.relating_object = None self.relating_object = None
+6 -6
View File
@@ -32,14 +32,14 @@ class BIM_PT_nest(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"): if not tool.Ifc.get().by_id(ifc_id).is_a("IfcObjectDefinition"):
return False return False
return True return True
@@ -55,7 +55,7 @@ class BIM_PT_nest(Panel):
row.prop(props, "relating_object", text="") row.prop(props, "relating_object", text="")
if props.relating_object: if props.relating_object:
op = row.operator("bim.nest_assign_object", icon="CHECKMARK", text="") op = row.operator("bim.nest_assign_object", icon="CHECKMARK", text="")
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id op.relating_object = tool.Blender.get_ifc_definition_id(props.relating_object)
row.operator("bim.disable_editing_nest", icon="CANCEL", text="") row.operator("bim.disable_editing_nest", icon="CANCEL", text="")
else: else:
row = layout.row(align=True) row = layout.row(align=True)
@@ -2327,11 +2327,12 @@ class RefreshClippingPlanes(bpy.types.Operator):
else: else:
break break
def is_moved(self, obj): def is_moved(self, obj: bpy.types.Object) -> bool:
if not obj.BIMObjectProperties.location_checksum: props = tool.Blender.get_object_bim_props(obj)
if not props.location_checksum:
return True # Let's be conservative return True # Let's be conservative
loc_check = np.frombuffer(eval(obj.BIMObjectProperties.location_checksum)) loc_check = np.frombuffer(eval(props.location_checksum))
rot_check = np.frombuffer(eval(obj.BIMObjectProperties.rotation_checksum)) rot_check = np.frombuffer(eval(props.rotation_checksum))
loc_real = np.array(obj.matrix_world.translation).flatten() loc_real = np.array(obj.matrix_world.translation).flatten()
rot_real = np.array(obj.matrix_world.to_3x3()).flatten() rot_real = np.array(obj.matrix_world.to_3x3()).flatten()
if np.allclose(loc_check, loc_real, atol=1e-4) and np.allclose(rot_check, rot_real, atol=1e-2): if np.allclose(loc_check, loc_real, atol=1e-4) and np.allclose(rot_check, rot_real, atol=1e-2):
+7 -12
View File
@@ -351,10 +351,9 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator, tool.Ifc.Operator):
props = context.scene.AddEditProperties props = context.scene.AddEditProperties
for obj in tool.Blender.get_selected_objects(): for obj in tool.Blender.get_selected_objects():
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id ifc_element = tool.Ifc.get_entity(obj)
if not ifc_definition_id: if not ifc_element:
continue continue
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
for prop in props: for prop in props:
value = getattr(prop, prop.get_value_name()) value = getattr(prop, prop.get_value_name())
@@ -406,23 +405,19 @@ class BIM_OT_bulk_remove_psets(bpy.types.Operator, tool.Ifc.Operator):
props = context.scene.DeletePsets props = context.scene.DeletePsets
for obj in tool.Blender.get_selected_objects(): for obj in tool.Blender.get_selected_objects():
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id ifc_element = tool.Ifc.get_entity(obj)
if not ifc_definition_id: if not ifc_element:
continue continue
ifc_element = tool.Ifc.get().by_id(ifc_definition_id)
psets = ifcopenshell.util.element.get_psets(ifc_element) psets = ifcopenshell.util.element.get_psets(ifc_element)
for prop in props: for prop in props:
pset = prop.pset_name pset = prop.pset_name
if pset in psets: if pset in psets:
try: try:
ifcopenshell.api.run( ifcopenshell.api.pset.remove_pset(
"pset.remove_pset",
self.file, self.file,
**{ product=ifc_element,
"product": self.file.by_id(ifc_definition_id), pset=self.file.by_id(psets[pset]["id"]),
"pset": self.file.by_id(psets[pset]["id"]),
},
) )
except KeyError: except KeyError:
pass # Sometimes the pset id is not found, I'm not sure why this happens though. - vulevukusej pass # Sometimes the pset id is not found, I'm not sure why this happens though. - vulevukusej
+8 -8
View File
@@ -241,12 +241,12 @@ class BIM_PT_object_psets(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
return True return True
@@ -319,12 +319,12 @@ class BIM_PT_object_qtos(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
return True return True
@@ -648,7 +648,8 @@ def get_opening_area(
""" """
total_opening_area = 0 total_opening_area = 0
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
ifc_element = ifc.by_id(obj.BIMObjectProperties.ifc_definition_id) ifc_element = tool.Ifc.get_entity(obj)
assert ifc_element
if len(openings := ifc_element.HasOpenings) != 0: if len(openings := ifc_element.HasOpenings) != 0:
for opening in openings: for opening in openings:
opening_id = opening.RelatedOpeningElement.GlobalId opening_id = opening.RelatedOpeningElement.GlobalId
@@ -887,7 +888,7 @@ def get_OBB_object(obj: bpy.types.Object) -> bpy.types.Object:
:param blender-object obj: Blender Object :param blender-object obj: Blender Object
:return blender-object: OBB of the Object :return blender-object: OBB of the Object
""" """
ifc_id = obj.BIMObjectProperties.ifc_definition_id ifc_id = tool.Blender.get_ifc_definition_id(obj)
bbox = obj.bound_box bbox = obj.bound_box
# matrix transformation to go from obj coordinates to world coordinates: # matrix transformation to go from obj coordinates to world coordinates:
obb = [Vector(v) for v in bbox] obb = [Vector(v) for v in bbox]
@@ -929,7 +930,7 @@ def get_AABB_object(obj: bpy.types.Object) -> bpy.types.Object:
:param blender-object obj: Blender Object :param blender-object obj: Blender Object
:return blender-object: AABB of the Object :return blender-object: AABB of the Object
""" """
ifc_id = obj.BIMObjectProperties.ifc_definition_id ifc_id = tool.Blender.get_ifc_definition_id(obj)
aabb_mesh = bpy.data.meshes.new(f"OBB_{ifc_id}") aabb_mesh = bpy.data.meshes.new(f"OBB_{ifc_id}")
x = [v.co.x for v in obj.data.vertices] x = [v.co.x for v in obj.data.vertices]
@@ -994,7 +995,7 @@ def get_bisected_obj(
:param tuple(x,y,z) plane_no_neg: Tuple describing the normal vector of the lower bisection plane. Example: (0,0,-1) :param tuple(x,y,z) plane_no_neg: Tuple describing the normal vector of the lower bisection plane. Example: (0,0,-1)
:return _type_: _description_ :return _type_: _description_
""" """
ifc_id = obj.BIMObjectProperties.ifc_definition_id ifc_id = tool.Blender.get_ifc_definition_id(obj)
bis_obj = obj.copy() bis_obj = obj.copy()
bis_obj.data = obj.data.copy() bis_obj.data = obj.data.copy()
+2 -1
View File
@@ -18,6 +18,7 @@
import bpy import bpy
import bmesh import bmesh
import bonsai.tool as tool
from typing import Callable from typing import Callable
@@ -86,7 +87,7 @@ def calculate_formwork_area(objs: list[bpy.types.Object], context: bpy.types.Con
bpy.ops.object.modifier_apply(modifier="Boolean") bpy.ops.object.modifier_apply(modifier="Boolean")
copied_obj.name = "Formwork" copied_obj.name = "Formwork"
copied_obj.BIMObjectProperties.ifc_definition_id = 0 tool.Blender.get_object_bim_props(copied_obj).ifc_definition_id = 0
modifier = copied_obj.modifiers.new("Formwork", "REMESH") modifier = copied_obj.modifiers.new("Formwork", "REMESH")
assert isinstance(modifier, bpy.types.RemeshModifier) assert isinstance(modifier, bpy.types.RemeshModifier)
modifier.mode = "SHARP" modifier.mode = "SHARP"
@@ -48,7 +48,8 @@ class EnableReassignClass(bpy.types.Operator):
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
assert element assert element
ifc_class = element.is_a() ifc_class = element.is_a()
context.active_object.BIMObjectProperties.is_reassigning_class = True props = tool.Blender.get_object_bim_props(obj)
props.is_reassigning_class = True
ifc_products = tool.Root.get_ifc_products() ifc_products = tool.Root.get_ifc_products()
schema = tool.Ifc.schema() schema = tool.Ifc.schema()
declaration = schema.declaration_by_name(ifc_class) declaration = schema.declaration_by_name(ifc_class)
@@ -58,10 +59,10 @@ class EnableReassignClass(bpy.types.Operator):
break break
else: else:
self.report({"ERROR"}, f"Couldn't find matching IFC product for the selected object: '{element}'.") self.report({"ERROR"}, f"Couldn't find matching IFC product for the selected object: '{element}'.")
obj.BIMObjectProperties.is_reassigning_class = False props.is_reassigning_class = False
return {"CANCELLED"} return {"CANCELLED"}
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) element = self.file.by_id(tool.Blender.get_ifc_definition_id(obj))
rprops.ifc_class = element.is_a() rprops.ifc_class = element.is_a()
rprops.relating_class_object = None rprops.relating_class_object = None
if hasattr(element, "PredefinedType"): if hasattr(element, "PredefinedType"):
@@ -78,7 +79,8 @@ class DisableReassignClass(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
context.active_object.BIMObjectProperties.is_reassigning_class = False props = tool.Blender.get_object_bim_props(context.active_object)
props.is_reassigning_class = False
return {"FINISHED"} return {"FINISHED"}
@@ -127,7 +129,8 @@ class ReassignClass(bpy.types.Operator, tool.Ifc.Operator):
) )
return {"CANCELLED"} return {"CANCELLED"}
obj.BIMObjectProperties.is_reassigning_class = False props = tool.Blender.get_object_bim_props(obj)
props.is_reassigning_class = False
if element.is_a("IfcTypeObject"): if element.is_a("IfcTypeObject"):
elements_to_reassign[element] = ifc_class elements_to_reassign[element] = ifc_class
elements_to_update.update(ifcopenshell.util.element.get_types(element)) elements_to_update.update(ifcopenshell.util.element.get_types(element))
+3 -1
View File
@@ -43,7 +43,9 @@ class BIM_PT_class(Panel):
def draw(self, context): def draw(self, context):
if not IfcClassData.is_loaded: if not IfcClassData.is_loaded:
IfcClassData.load() IfcClassData.load()
props = context.active_object.BIMObjectProperties obj = context.active_object
assert obj
props = tool.Blender.get_object_bim_props(obj)
rprops = tool.Root.get_root_props() rprops = tool.Root.get_root_props()
if props.ifc_definition_id: if props.ifc_definition_id:
if not IfcClassData.data["has_entity"]: if not IfcClassData.data["has_entity"]:
@@ -1449,15 +1449,17 @@ class LoadProductTasks(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not tool.Ifc.get() or not (obj := context.active_object) or not (obj.BIMObjectProperties.ifc_definition_id): if not tool.Ifc.get() or not (obj := context.active_object) or not (tool.Blender.get_ifc_definition_id(obj)):
cls.poll_message_set("No IFC object is active.") cls.poll_message_set("No IFC object is active.")
return False return False
return True return True
def execute(self, context): def execute(self, context):
result = core.load_product_related_tasks( obj = context.active_object
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id) assert obj
) product = tool.Ifc.get_entity(obj)
assert product
result = core.load_product_related_tasks(tool.Sequence, product=product)
if isinstance(result, str): if isinstance(result, str):
self.report({"INFO"}, result) self.report({"INFO"}, result)
else: else:
@@ -271,10 +271,10 @@ def update_sort_reversed(self, context):
def update_filter_by_active_schedule(self, context): def update_filter_by_active_schedule(self, context):
if context.active_object: if obj := context.active_object:
core.load_product_related_tasks( product = tool.Ifc.get_entity(obj)
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id) assert product
) core.load_product_related_tasks(tool.Sequence, product=product)
def switch_options(self, context): def switch_options(self, context):
@@ -75,11 +75,13 @@ class AddStructuralMemberConnection(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties assert obj
oprops = tool.Blender.get_object_bim_props(obj)
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
file = tool.Ifc.get() file = tool.Ifc.get()
related_structural_connection = file.by_id(oprops.ifc_definition_id) related_structural_connection = file.by_id(oprops.ifc_definition_id)
relating_structural_member = file.by_id(props.relating_structural_member.BIMObjectProperties.ifc_definition_id) relating_structural_member = tool.Ifc.get_entity(props.relating_structural_member)
assert relating_structural_member
if not relating_structural_member.is_a("IfcStructuralMember"): if not relating_structural_member.is_a("IfcStructuralMember"):
return {"FINISHED"} return {"FINISHED"}
ifcopenshell.api.structural.add_structural_member_connection( ifcopenshell.api.structural.add_structural_member_connection(
@@ -99,7 +101,6 @@ class EnableEditingStructuralConnectionCondition(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
props.active_connects_structural_member = self.connects_structural_member props.active_connects_structural_member = self.connects_structural_member
return {"FINISHED"} return {"FINISHED"}
@@ -350,7 +351,8 @@ class EnableEditingStructuralItemAxis(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties assert obj
oprops = tool.Blender.get_object_bim_props(obj)
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
@@ -403,7 +405,8 @@ class EditStructuralItemAxis(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties assert obj
oprops = tool.Blender.get_object_bim_props(obj)
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
relative_matrix = props.axis_empty.matrix_world @ obj.matrix_world.inverted() relative_matrix = props.axis_empty.matrix_world @ obj.matrix_world.inverted()
z_axis = relative_matrix.col[2][0:3] z_axis = relative_matrix.col[2][0:3]
@@ -425,11 +428,12 @@ class EnableEditingStructuralConnectionCS(bpy.types.Operator):
def execute(self, context): def execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties assert obj
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
item = self.file.by_id(oprops.ifc_definition_id) item = tool.Ifc.get_entity(obj)
assert item
location = obj.data.vertices[0].co location = obj.data.vertices[0].co
empty = bpy.data.objects.new("Item Connection CS", None) empty = bpy.data.objects.new("Item Connection CS", None)
@@ -491,16 +495,17 @@ class EditStructuralConnectionCS(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = context.active_object obj = context.active_object
oprops = obj.BIMObjectProperties assert obj
item = tool.Ifc.get_entity(obj)
assert item
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
relative_matrix = props.ccs_empty.matrix_world @ obj.matrix_world.inverted() relative_matrix = props.ccs_empty.matrix_world @ obj.matrix_world.inverted()
x_axis = relative_matrix.col[0][0:3] x_axis = relative_matrix.col[0][0:3]
z_axis = relative_matrix.col[2][0:3] z_axis = relative_matrix.col[2][0:3]
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
ifcopenshell.api.run( ifcopenshell.api.structural.edit_structural_connection_cs(
"structural.edit_structural_connection_cs",
self.file, self.file,
structural_item=self.file.by_id(oprops.ifc_definition_id), structural_item=item,
axis=z_axis, axis=z_axis,
ref_direction=x_axis, ref_direction=x_axis,
) )
@@ -695,9 +700,9 @@ class AddStructuralActivity(bpy.types.Operator, tool.Ifc.Operator):
self.props = context.scene.BIMStructuralProperties self.props = context.scene.BIMStructuralProperties
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
for obj in context.selected_objects: for obj in context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id: element = tool.Ifc.get_entity(obj)
if not element:
continue continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
applied_load_class = self.props.applicable_structural_load_types applied_load_class = self.props.applicable_structural_load_types
allowed_load_classes = { allowed_load_classes = {
+20 -21
View File
@@ -85,14 +85,14 @@ class BIM_PT_structural_boundary_conditions(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not (tool.Ifc.get_object_by_identifier(ifc_id)):
return False return False
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcStructuralConnection"): if not tool.Ifc.get().by_id(ifc_id).is_a("IfcStructuralConnection"):
return False return False
return True return True
@@ -120,14 +120,14 @@ class BIM_PT_connected_structural_members(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not (tool.Ifc.get_object_by_identifier(ifc_id)):
return False return False
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcStructuralConnection"): if not tool.Ifc.get().by_id(ifc_id).is_a("IfcStructuralConnection"):
return False return False
return True return True
@@ -173,14 +173,14 @@ class BIM_PT_structural_member(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcStructuralMember"): if not tool.Ifc.get().by_id(ifc_id).is_a("IfcStructuralMember"):
return False return False
return True return True
@@ -216,14 +216,14 @@ class BIM_PT_structural_connection(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.active_object: if not (obj := context.active_object):
return False return False
props = context.active_object.BIMObjectProperties ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not props.ifc_definition_id: if not ifc_id:
return False return False
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id): if not tool.Ifc.get_object_by_identifier(ifc_id):
return False return False
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcStructuralConnection"): if not tool.Ifc.get().by_id(ifc_id).is_a("IfcStructuralConnection"):
return False return False
return True return True
@@ -315,7 +315,6 @@ class BIM_UL_structural_analysis_models(UIList):
row.label(text=item.name) row.label(text=item.name)
if context.active_object: if context.active_object:
oprops = context.active_object.BIMObjectProperties
if item.ifc_definition_id in StructuralAnalysisModelsData.data["active_model_ids"]: if item.ifc_definition_id in StructuralAnalysisModelsData.data["active_model_ids"]:
op = row.operator( op = row.operator(
"bim.unassign_structural_analysis_model", text="", icon="KEYFRAME_HLT", emboss=False "bim.unassign_structural_analysis_model", text="", icon="KEYFRAME_HLT", emboss=False
+8 -12
View File
@@ -187,9 +187,8 @@ class BIM_PT_ports(Panel):
if connected_obj_name: if connected_obj_name:
connected_obj = bpy.data.objects[connected_obj_name] connected_obj = bpy.data.objects[connected_obj_name]
cols[3].operator("bim.disconnect_port", text="", icon="UNLINKED").element_id = port.id() cols[3].operator("bim.disconnect_port", text="", icon="UNLINKED").element_id = port.id()
cols[4].operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ( ifc_id = tool.Blender.get_ifc_definition_id(connected_obj)
connected_obj.BIMObjectProperties.ifc_definition_id cols[4].operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ifc_id
)
cols[5].label(text=connected_obj_name) cols[5].label(text=connected_obj_name)
else: else:
cols[3].label(text="", icon="UNLINKED") cols[3].label(text="", icon="UNLINKED")
@@ -244,9 +243,8 @@ class BIM_PT_port(Panel):
relating_object = bpy.data.objects[relating_object_name] relating_object = bpy.data.objects[relating_object_name]
row.label(text="Port located on:") row.label(text="Port located on:")
row.label(text=relating_object_name) row.label(text=relating_object_name)
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ( ifc_id = tool.Blender.get_ifc_definition_id(relating_object)
relating_object.BIMObjectProperties.ifc_definition_id row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ifc_id
)
# object connected to the port # object connected to the port
row = layout.row(align=True) row = layout.row(align=True)
@@ -255,9 +253,8 @@ class BIM_PT_port(Panel):
connected_object = bpy.data.objects[connected_object_name] connected_object = bpy.data.objects[connected_object_name]
row.label(text="Port connected to:") row.label(text="Port connected to:")
row.label(text=connected_object_name) row.label(text=connected_object_name)
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ( ifc_id = tool.Blender.get_ifc_definition_id(connected_object)
connected_object.BIMObjectProperties.ifc_definition_id row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ifc_id
)
else: else:
row.label(text="Port is not connected to any element") row.label(text="Port is not connected to any element")
@@ -304,9 +301,8 @@ class BIM_PT_flow_controls(Panel):
op.flow_control = control_id op.flow_control = control_id
op.flow_element = flow_element_id op.flow_element = flow_element_id
op.assign = False op.assign = False
row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ( ifc_id = tool.Blender.get_ifc_definition_id(displayed_object)
displayed_object.BIMObjectProperties.ifc_definition_id row.operator("bim.select_entity", text="", icon="RESTRICT_SELECT_OFF").ifc_id = ifc_id
)
row.label(text=f"{displayed_object_name}") row.label(text=f"{displayed_object_name}")
element = tool.Ifc.get_entity(context.active_object) element = tool.Ifc.get_entity(context.active_object)
@@ -150,7 +150,8 @@ class SelectRequirement(bpy.types.Operator):
area.spaces[0].shading.show_xray = True area.spaces[0].shading.show_xray = True
failed_ids = [e["id"] for e in failed_entities] failed_ids = [e["id"] for e in failed_entities]
for obj in context.scene.objects: for obj in context.scene.objects:
if obj.BIMObjectProperties.ifc_definition_id in failed_ids: ifc_id = tool.Blender.get_ifc_definition_id(obj)
if ifc_id in failed_ids:
obj.color = (1, 0, 0, 1) obj.color = (1, 0, 0, 1)
else: else:
obj.color = (1, 1, 1, 1) obj.color = (1, 1, 1, 1)
@@ -175,7 +176,8 @@ class SelectFailedEntities(bpy.types.Operator):
failed_ids = [e["id"] for e in failed_entities] failed_ids = [e["id"] for e in failed_entities]
for obj in context.scene.objects: for obj in context.scene.objects:
if obj.BIMObjectProperties.ifc_definition_id in failed_ids: ifc_id = tool.Blender.get_ifc_definition_id(obj)
if ifc_id in failed_ids:
obj.select_set(True) obj.select_set(True)
else: else:
obj.select_set(False) obj.select_set(False)
@@ -337,5 +337,5 @@ class DuplicateType(bpy.types.Operator, tool.Ifc.Operator):
# Set duplicated type as active in current tool. # Set duplicated type as active in current tool.
if ifc_class in (i[0] for i in (bonsai.bim.helper.get_enum_items(props, "ifc_class", context) or ()) if i): if ifc_class in (i[0] for i in (bonsai.bim.helper.get_enum_items(props, "ifc_class", context) or ()) if i):
props.ifc_class = new.is_a() props.ifc_class = new.is_a()
props.relating_type_id = str(new_obj.BIMObjectProperties.ifc_definition_id) props.relating_type_id = str(tool.Blender.get_ifc_definition_id(new_obj))
return {"FINISHED"} return {"FINISHED"}
+5 -5
View File
@@ -46,7 +46,9 @@ class BIM_PT_type(Panel):
if not TypeData.is_loaded: if not TypeData.is_loaded:
TypeData.load() TypeData.load()
oprops = context.active_object.BIMObjectProperties obj = context.active_object
assert obj
oprops = tool.Blender.get_object_bim_props(obj)
if TypeData.data["is_product"]: if TypeData.data["is_product"]:
self.draw_product_ui(context) self.draw_product_ui(context)
@@ -54,21 +56,19 @@ class BIM_PT_type(Panel):
self.draw_type_ui(context) self.draw_type_ui(context)
def draw_type_ui(self, context): def draw_type_ui(self, context):
props = context.active_object.BIMTypeProperties oprops = tool.Blender.get_object_bim_props(context.active_object)
oprops = context.active_object.BIMObjectProperties
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=f"{TypeData.data['total_instances']} Typed Objects") row.label(text=f"{TypeData.data['total_instances']} Typed Objects")
select_type_objects_row = row.row(align=True) select_type_objects_row = row.row(align=True)
select_type_objects_row.operator("bim.select_type_objects", icon="RESTRICT_SELECT_OFF", text="") select_type_objects_row.operator("bim.select_type_objects", icon="RESTRICT_SELECT_OFF", text="")
select_type_objects_row.enabled = int(TypeData.data["total_instances"]) > 0 select_type_objects_row.enabled = int(TypeData.data["total_instances"]) > 0
op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="") op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="")
op.element = context.active_object.BIMObjectProperties.ifc_definition_id op.element = oprops.ifc_definition_id
row.operator("bim.auto_rename_occurrences", icon="ITALIC", text="") row.operator("bim.auto_rename_occurrences", icon="ITALIC", text="")
def draw_product_ui(self, context): def draw_product_ui(self, context):
layout = self.layout layout = self.layout
props = context.active_object.BIMTypeProperties props = context.active_object.BIMTypeProperties
oprops = context.active_object.BIMObjectProperties
if props.is_editing_type: if props.is_editing_type:
row = layout.row(align=True) row = layout.row(align=True)
@@ -215,8 +215,8 @@ class AddFilling(bpy.types.Operator, tool.Ifc.Operator):
if opening is None: if opening is None:
return {"FINISHED"} return {"FINISHED"}
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
element_id = obj.BIMObjectProperties.ifc_definition_id element_id = tool.Blender.get_object_bim_props(obj).ifc_definition_id
opening_id = opening.BIMObjectProperties.ifc_definition_id opening_id = tool.Blender.get_object_bim_props(opening).ifc_definition_id
if not element_id or not opening_id or element_id == opening_id: if not element_id or not opening_id or element_id == opening_id:
return {"FINISHED"} return {"FINISHED"}
ifcopenshell.api.run( ifcopenshell.api.run(
-2
View File
@@ -52,8 +52,6 @@ class BIM_PT_voids(Panel):
if not VoidsData.is_loaded: if not VoidsData.is_loaded:
VoidsData.load() VoidsData.load()
props = context.active_object.BIMObjectProperties
if len(context.selected_objects) >= 2: if len(context.selected_objects) >= 2:
row = self.layout.row(align=True) row = self.layout.row(align=True)
op = row.operator("bim.add_opening", icon="ADD", text="Add Opening") op = row.operator("bim.add_opening", icon="ADD", text="Add Opening")
+3 -1
View File
@@ -857,7 +857,9 @@ class FetchObjectPassport(bpy.types.Operator):
def execute(self, context): def execute(self, context):
# TODO: this is dead code, awaiting reimplementation. See #1222. # TODO: this is dead code, awaiting reimplementation. See #1222.
for reference in context.active_object.BIMObjectProperties.document_references: obj = context.active_object
props = tool.Blender.get_object_bim_props(obj)
for reference in props.document_references:
bim_props = tool.Blender.get_bim_props() bim_props = tool.Blender.get_bim_props()
reference = bim_props.document_references[reference.name] reference = bim_props.document_references[reference.name]
if reference.location[-6:] == ".blend": if reference.location[-6:] == ".blend":
+26 -10
View File
@@ -41,7 +41,7 @@ from typing import Any, Optional, Union, Literal, Iterable, Callable, TypeVar, G
from typing_extensions import assert_never from typing_extensions import assert_never
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.prop import BIMProperties from bonsai.bim.prop import BIMProperties, BIMObjectProperties
VIEWPORT_ATTRIBUTES = [ VIEWPORT_ATTRIBUTES = [
@@ -192,7 +192,8 @@ class Blender(bonsai.core.tool.Blender):
if context is None: if context is None:
context = bpy.context context = bpy.context
if obj_type == "Object": if obj_type == "Object":
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id props = tool.Blender.get_object_bim_props(bpy.data.objects[obj])
return props.ifc_definition_id
elif obj_type == "Material": elif obj_type == "Material":
props = tool.Material.get_material_props() props = tool.Material.get_material_props()
return props.materials[props.active_material_index].ifc_definition_id return props.materials[props.active_material_index].ifc_definition_id
@@ -221,7 +222,8 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def is_ifc_object(cls, obj: bpy.types.Object) -> bool: def is_ifc_object(cls, obj: bpy.types.Object) -> bool:
return bool(obj.BIMObjectProperties.ifc_definition_id) props = tool.Blender.get_object_bim_props(obj)
return bool(props.ifc_definition_id)
@classmethod @classmethod
def is_ifc_class_active(cls, ifc_class: str) -> bool: def is_ifc_class_active(cls, ifc_class: str) -> bool:
@@ -880,7 +882,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def get_layer_collection(cls, collection: bpy.types.Collection) -> Union[bpy.types.LayerCollection, None]: 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 = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection project_collection = tool.Blender.get_object_bim_props(project).collection
for layer_collection in bpy.context.view_layer.layer_collection.children: for layer_collection in bpy.context.view_layer.layer_collection.children:
if layer_collection.collection == project_collection: if layer_collection.collection == project_collection:
for layer_collection2 in layer_collection.children: for layer_collection2 in layer_collection.children:
@@ -1019,23 +1021,28 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def is_editing_railing_path(cls, obj: bpy.types.Object): def is_editing_railing_path(cls, obj: bpy.types.Object):
return obj.BIMRailingProperties.is_editing_path props = tool.Model.get_railing_props(obj)
return props.is_editing_path
@classmethod @classmethod
def is_editing_roof_path(cls, obj: bpy.types.Object) -> bool: def is_editing_roof_path(cls, obj: bpy.types.Object) -> bool:
return obj.BIMRoofProperties.is_editing_path props = tool.Model.get_roof_props(obj)
return props.is_editing_path
@classmethod @classmethod
def is_editing_railing_parameters(cls, obj: bpy.types.Object) -> bool: def is_editing_railing_parameters(cls, obj: bpy.types.Object) -> bool:
return obj.BIMRailingProperties.is_editing props = tool.Model.get_railing_props(obj)
return props.is_editing
@classmethod @classmethod
def is_editing_roof_parameters(cls, obj: bpy.types.Object) -> bool: def is_editing_roof_parameters(cls, obj: bpy.types.Object) -> bool:
return obj.BIMRoofProperties.is_editing props = tool.Model.get_roof_props(obj)
return props.is_editing
@classmethod @classmethod
def is_editing_window_parameters(cls, obj: bpy.types.Object) -> bool: def is_editing_window_parameters(cls, obj: bpy.types.Object) -> bool:
return obj.BIMWindowProperties.is_editing props = tool.Model.get_window_props(obj)
return props.is_editing
@classmethod @classmethod
def is_editing_door_parameters(cls, obj: bpy.types.Object) -> bool: def is_editing_door_parameters(cls, obj: bpy.types.Object) -> bool:
@@ -1044,7 +1051,8 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def is_editing_stair_parameters(cls, obj: bpy.types.Object) -> bool: def is_editing_stair_parameters(cls, obj: bpy.types.Object) -> bool:
return obj.BIMStairProperties.is_editing props = tool.Model.get_stair_props(obj)
return props.is_editing
@classmethod @classmethod
def is_modifier_with_non_editable_path(cls, element: entity_instance) -> bool: def is_modifier_with_non_editable_path(cls, element: entity_instance) -> bool:
@@ -1545,3 +1553,11 @@ class Blender(bonsai.core.tool.Blender):
if scene is None: if scene is None:
scene = bpy.context.scene scene = bpy.context.scene
return scene.BIMProperties return scene.BIMProperties
@classmethod
def get_object_bim_props(cls, obj: bpy.types.Object) -> BIMObjectProperties:
return obj.BIMObjectProperties
@classmethod
def get_ifc_definition_id(cls, obj: bpy.types.Object) -> int:
return tool.Blender.get_object_bim_props(obj).ifc_definition_id
+9 -1
View File
@@ -28,7 +28,7 @@
# - An arc is reconstructed from 3 points instead of a full circle # - An arc is reconstructed from 3 points instead of a full circle
# - You can now derive the center from an arc without generating geometry # - You can now derive the center from an arc without generating geometry
from __future__ import annotations
import sys import sys
import bpy import bpy
import math import math
@@ -36,12 +36,20 @@ import bmesh
import mathutils.geometry import mathutils.geometry
from mathutils import Vector, Matrix, geometry from mathutils import Vector, Matrix, geometry
import itertools import itertools
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.cad.prop import BIMCadProperties
VTX_PRECISION = 1.0e-5 VTX_PRECISION = 1.0e-5
class Cad: class Cad:
@classmethod
def get_cad_props(cls) -> BIMCadProperties:
return bpy.context.scene.BIMCadProperties
@classmethod @classmethod
def is_point_on_edge(cls, p, edge): def is_point_on_edge(cls, p, edge):
""" """
+14 -12
View File
@@ -29,7 +29,7 @@ class Collector(bonsai.core.tool.Collector):
"""Links an object to an appropriate Blender collection.""" """Links an object to an appropriate Blender collection."""
if should_clean_users_collection: if should_clean_users_collection:
for users_collection in obj.users_collection: for users_collection in obj.users_collection:
if obj.BIMObjectProperties.collection == users_collection: if tool.Blender.get_object_bim_props(obj).collection == users_collection:
continue continue
# Users are free to use extra collections for their own # Users are free to use extra collections for their own
# purposes except for the reserved keyword "Ifc" and # purposes except for the reserved keyword "Ifc" and
@@ -87,7 +87,7 @@ class Collector(bonsai.core.tool.Collector):
if collection := cls._create_own_collection(obj): if collection := cls._create_own_collection(obj):
cls.link_collection_object_safe(collection, obj) cls.link_collection_object_safe(collection, obj)
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
cls.link_collection_child_safe(project_obj.BIMObjectProperties.collection, collection) cls.link_collection_child_safe(tool.Blender.get_object_bim_props(project_obj).collection, collection)
elif ( elif (
tool.Ifc.get_schema() != "IFC2X3" tool.Ifc.get_schema() != "IFC2X3"
and element.is_a("IfcSpatialElement") and element.is_a("IfcSpatialElement")
@@ -98,21 +98,21 @@ class Collector(bonsai.core.tool.Collector):
if collection := cls._create_own_collection(obj): if collection := cls._create_own_collection(obj):
cls.link_collection_object_safe(collection, obj) cls.link_collection_object_safe(collection, obj)
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
cls.link_collection_child_safe(project_obj.BIMObjectProperties.collection, collection) cls.link_collection_child_safe(tool.Blender.get_object_bim_props(project_obj).collection, collection)
elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
if collection := cls._create_own_collection(obj): if collection := cls._create_own_collection(obj):
cls.link_collection_object_safe(collection, obj) cls.link_collection_object_safe(collection, obj)
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
cls.link_collection_child_safe(project_obj.BIMObjectProperties.collection, collection) cls.link_collection_child_safe(tool.Blender.get_object_bim_props(project_obj).collection, collection)
elif element.is_a("IfcAnnotation") and (drawing_obj := cls.get_annotation_drawing_obj(element)): elif element.is_a("IfcAnnotation") and (drawing_obj := cls.get_annotation_drawing_obj(element)):
cls.link_collection_object_safe(drawing_obj.BIMObjectProperties.collection, obj) cls.link_collection_object_safe(tool.Blender.get_object_bim_props(drawing_obj).collection, obj)
elif container := ifcopenshell.util.element.get_container(element): elif container := ifcopenshell.util.element.get_container(element):
while container.is_a("IfcSpace"): while container.is_a("IfcSpace"):
container = ifcopenshell.util.element.get_aggregate(container) container = ifcopenshell.util.element.get_aggregate(container)
container_obj = tool.Ifc.get_object(container) container_obj = tool.Ifc.get_object(container)
if not (collection := container_obj.BIMObjectProperties.collection): if not (collection := tool.Blender.get_object_bim_props(container_obj).collection):
cls.assign(container_obj) cls.assign(container_obj)
collection = container_obj.BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(container_obj).collection
cls.link_collection_object_safe(collection, obj) cls.link_collection_object_safe(collection, obj)
else: else:
collection = cls._create_project_child_collection("Unsorted") collection = cls._create_project_child_collection("Unsorted")
@@ -128,7 +128,7 @@ class Collector(bonsai.core.tool.Collector):
return collection return collection
collection = bpy.data.collections.new(name) collection = bpy.data.collections.new(name)
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_obj.BIMObjectProperties.collection.children.link(collection) tool.Blender.get_object_bim_props(project_obj).collection.children.link(collection)
if layer_collection := tool.Blender.get_layer_collection(collection): if layer_collection := tool.Blender.get_layer_collection(collection):
cls.set_layer_collection_visibility(layer_collection) cls.set_layer_collection_visibility(layer_collection)
return collection return collection
@@ -136,11 +136,12 @@ class Collector(bonsai.core.tool.Collector):
@classmethod @classmethod
def _create_own_collection(cls, obj: bpy.types.Object) -> bpy.types.Collection: def _create_own_collection(cls, obj: bpy.types.Object) -> bpy.types.Collection:
"""get or create own collection for the element""" """get or create own collection for the element"""
if obj.BIMObjectProperties.collection: props = tool.Blender.get_object_bim_props(obj)
obj.BIMObjectProperties.collection.name = obj.name if props.collection:
props.collection.name = obj.name
return return
collection = bpy.data.collections.new(obj.name) collection = bpy.data.collections.new(obj.name)
obj.BIMObjectProperties.collection = collection props.collection = collection
collection.BIMCollectionProperties.obj = obj collection.BIMCollectionProperties.obj = obj
return collection return collection
@@ -184,7 +185,8 @@ class Collector(bonsai.core.tool.Collector):
@classmethod @classmethod
def reset_default_visibility(cls) -> None: def reset_default_visibility(cls) -> None:
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection assert project
project_collection = tool.Blender.get_object_bim_props(project).collection
for layer_collection in bpy.context.view_layer.layer_collection.children: for layer_collection in bpy.context.view_layer.layer_collection.children:
if layer_collection.collection == project_collection: if layer_collection.collection == project_collection:
for layer_collection2 in layer_collection.children: for layer_collection2 in layer_collection.children:
+10 -1
View File
@@ -16,17 +16,26 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.covering.prop import BIMCoveringProperties
class Covering(bonsai.core.tool.Covering): class Covering(bonsai.core.tool.Covering):
@classmethod
def get_covering_props(cls) -> BIMCoveringProperties:
return bpy.context.scene.BIMCoveringProperties
@classmethod @classmethod
def get_z_from_ceiling_height(cls) -> float: def get_z_from_ceiling_height(cls) -> float:
props = bpy.context.scene.BIMCoveringProperties props = cls.get_covering_props()
return props.ceiling_height return props.ceiling_height
# def toggle_spaces_visibility_wired_and_textured(cls, spaces): # def toggle_spaces_visibility_wired_and_textured(cls, spaces):
+8 -7
View File
@@ -524,7 +524,7 @@ class Drawing(bonsai.core.tool.Drawing):
def get_drawing_collection(cls, drawing: ifcopenshell.entity_instance) -> Union[bpy.types.Collection, None]: def get_drawing_collection(cls, drawing: ifcopenshell.entity_instance) -> Union[bpy.types.Collection, None]:
obj = tool.Ifc.get_object(drawing) obj = tool.Ifc.get_object(drawing)
if obj: if obj:
return obj.BIMObjectProperties.collection return tool.Blender.get_object_bim_props(obj).collection
@classmethod @classmethod
def get_drawing_group(cls, drawing: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: def get_drawing_group(cls, drawing: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
@@ -1516,8 +1516,8 @@ class Drawing(bonsai.core.tool.Drawing):
dst = src.copy() dst = src.copy()
dst.data = dst.data.copy() dst.data = dst.data.copy()
dst.name = dst.name.replace("IfcGridAxis/", "") dst.name = dst.name.replace("IfcGridAxis/", "")
dst.BIMObjectProperties.ifc_definition_id = 0 tool.Blender.get_object_bim_props(dst).ifc_definition_id = 0
tool.Geometry.get_geometry_props(dst).ifc_definition_id = 0 tool.Geometry.get_geometry_props(dst.data).ifc_definition_id = 0
return dst return dst
def disassemble(obj: bpy.types.Object) -> tuple[bpy.types.Object, bmesh.types.BMesh]: def disassemble(obj: bpy.types.Object) -> tuple[bpy.types.Object, bmesh.types.BMesh]:
@@ -1888,7 +1888,7 @@ class Drawing(bonsai.core.tool.Drawing):
return bool( return bool(
camera is not None camera is not None
and camera.type == "CAMERA" and camera.type == "CAMERA"
and camera.BIMObjectProperties.ifc_definition_id and tool.Blender.get_ifc_definition_id(camera)
and area is not None and area is not None
) )
@@ -1910,11 +1910,12 @@ class Drawing(bonsai.core.tool.Drawing):
def isolate_camera_collection(cls, camera: bpy.types.Object) -> None: def isolate_camera_collection(cls, camera: bpy.types.Object) -> None:
drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"] drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
drawing_collections = [] drawing_collections = []
camera_collection = camera.BIMObjectProperties.collection camera_collection = tool.Blender.get_object_bim_props(camera).collection
for drawing in drawings: for drawing in drawings:
if not (drawing_obj := tool.Ifc.get_object(drawing)): if not (drawing_obj := tool.Ifc.get_object(drawing)):
continue continue
if not (drawing_collection := drawing_obj.BIMObjectProperties.collection): oprops = tool.Blender.get_object_bim_props(drawing_obj)
if not (drawing_collection := oprops.collection):
continue continue
if drawing_obj == camera: if drawing_obj == camera:
drawing_collection.hide_render = False drawing_collection.hide_render = False
@@ -1922,7 +1923,7 @@ class Drawing(bonsai.core.tool.Drawing):
drawing_collection.hide_render = True drawing_collection.hide_render = True
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection project_collection = tool.Blender.get_object_bim_props(project).collection
for layer_collection in bpy.context.view_layer.layer_collection.children: for layer_collection in bpy.context.view_layer.layer_collection.children:
if layer_collection.collection == project_collection: if layer_collection.collection == project_collection:
for layer_collection2 in layer_collection.children: for layer_collection2 in layer_collection.children:
+11 -11
View File
@@ -235,7 +235,7 @@ class Geometry(bonsai.core.tool.Geometry):
bpy.data.objects.remove(axis_obj) bpy.data.objects.remove(axis_obj)
ifcopenshell.api.grid.remove_grid_axis(tool.Ifc.get(), axis=axis) ifcopenshell.api.grid.remove_grid_axis(tool.Ifc.get(), axis=axis)
collection = obj.BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(obj).collection
if collection: if collection:
parent = ifcopenshell.util.element.get_aggregate(element) parent = ifcopenshell.util.element.get_aggregate(element)
if not parent: if not parent:
@@ -243,7 +243,7 @@ class Geometry(bonsai.core.tool.Geometry):
if parent: if parent:
parent_obj = tool.Ifc.get_object(parent) parent_obj = tool.Ifc.get_object(parent)
if parent_obj: if parent_obj:
parent_collection = parent_obj.BIMObjectProperties.collection parent_collection = tool.Blender.get_object_bim_props(parent_obj).collection
for child in collection.children: for child in collection.children:
parent_collection.children.link(child) parent_collection.children.link(child)
for child_object in collection.objects: for child_object in collection.objects:
@@ -557,11 +557,9 @@ class Geometry(bonsai.core.tool.Geometry):
@classmethod @classmethod
def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> npt.NDArray[np.float64] | None: def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> npt.NDArray[np.float64] | None:
if ( props = tool.Blender.get_object_bim_props(obj)
obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT" if props.blender_offset_type == "CARTESIAN_POINT" and props.cartesian_point_offset:
and obj.BIMObjectProperties.cartesian_point_offset return np.array(tuple(map(float, props.cartesian_point_offset.split(","))))
):
return np.array(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(","))))
@classmethod @classmethod
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
@@ -1084,8 +1082,9 @@ class Geometry(bonsai.core.tool.Geometry):
@classmethod @classmethod
def record_object_position(cls, obj: bpy.types.Object) -> None: def record_object_position(cls, obj: bpy.types.Object) -> None:
# These are recorded separately because they have different numerical tolerances # These are recorded separately because they have different numerical tolerances
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes()) props = tool.Blender.get_object_bim_props(obj)
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes()) props.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
props.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
@classmethod @classmethod
def remove_connection(cls, connection: ifcopenshell.entity_instance) -> None: def remove_connection(cls, connection: ifcopenshell.entity_instance) -> None:
@@ -1570,8 +1569,9 @@ class Geometry(bonsai.core.tool.Geometry):
def get_blender_offset_type(cls, obj: bpy.types.Object) -> Optional[str]: def get_blender_offset_type(cls, obj: bpy.types.Object) -> Optional[str]:
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
if props.has_blender_offset: if props.has_blender_offset:
if (result := obj.BIMObjectProperties.blender_offset_type) == "NONE": props = tool.Blender.get_object_bim_props(obj)
result = obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT" if (result := props.blender_offset_type) == "NONE":
result = props.blender_offset_type = "OBJECT_PLACEMENT"
return result return result
@classmethod @classmethod
+5 -4
View File
@@ -77,13 +77,14 @@ class Ifc(bonsai.core.tool.Ifc):
return False return False
if element and (element.is_a("IfcTypeProduct") or element.is_a("IfcProject")): if element and (element.is_a("IfcTypeProduct") or element.is_a("IfcProject")):
return False return False
if not obj.BIMObjectProperties.location_checksum: oprops = tool.Blender.get_object_bim_props(obj)
if not oprops.location_checksum:
return True # Let's be conservative return True # Let's be conservative
loc_check = np.frombuffer(eval(obj.BIMObjectProperties.location_checksum)) loc_check = np.frombuffer(eval(oprops.location_checksum))
loc_real = np.array(obj.matrix_world.translation).flatten() loc_real = np.array(obj.matrix_world.translation).flatten()
if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm if not np.allclose(loc_check, loc_real, atol=1e-4): # 0.1 mm
return True return True
rot_check = np.frombuffer(eval(obj.BIMObjectProperties.rotation_checksum)).reshape(3, 3) rot_check = np.frombuffer(eval(oprops.rotation_checksum)).reshape(3, 3)
rot_real = np.array(obj.matrix_world.to_3x3()) rot_real = np.array(obj.matrix_world.to_3x3())
rot_dot = np.dot(rot_check, rot_real.T) rot_dot = np.dot(rot_check, rot_real.T)
angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1)) angle_rad = np.arccos(np.clip((np.trace(rot_dot) - 1) / 2, -1, 1))
@@ -107,7 +108,7 @@ class Ifc(bonsai.core.tool.Ifc):
props = None props = None
if isinstance(obj, bpy.types.Object): if isinstance(obj, bpy.types.Object):
props = obj.BIMObjectProperties props = tool.Blender.get_object_bim_props(obj)
elif isinstance(obj, bpy.types.Material): elif isinstance(obj, bpy.types.Material):
props = obj.BIMStyleProperties props = obj.BIMStyleProperties
else: else:
+2 -2
View File
@@ -397,9 +397,9 @@ class IfcGit:
bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_all(action="DESELECT")
for obj in bpy.context.visible_objects: for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id: props = tool.Blender.get_object_bim_props(obj)
if not (step_id := props.ifc_definition_id):
continue continue
step_id = obj.BIMObjectProperties.ifc_definition_id
if step_id in step_ids["modified"]: if step_id in step_ids["modified"]:
obj.color = (0.3, 0.3, 1.0, 1) obj.color = (0.3, 0.3, 1.0, 1)
obj.select_set(True) obj.select_set(True)
+6 -5
View File
@@ -929,6 +929,7 @@ class Loader(bonsai.core.tool.Loader):
@classmethod @classmethod
def apply_blender_offset_to_matrix_world(cls, obj: bpy.types.Object, matrix: np.ndarray) -> Matrix: def apply_blender_offset_to_matrix_world(cls, obj: bpy.types.Object, matrix: np.ndarray) -> Matrix:
oprops = tool.Blender.get_object_bim_props(obj)
if ( if (
not obj.data not obj.data
and tool.Cad.is_x(matrix[0][3], 0) and tool.Cad.is_x(matrix[0][3], 0)
@@ -939,13 +940,13 @@ class Loader(bonsai.core.tool.Loader):
# positionally significant and is left alone. This handles # positionally significant and is left alone. This handles
# scenarios where often spatial elements are left at 0,0,0 and # scenarios where often spatial elements are left at 0,0,0 and
# everything else is at map coordinates. # everything else is at map coordinates.
obj.BIMObjectProperties.blender_offset_type = "NOT_APPLICABLE" oprops.blender_offset_type = "NOT_APPLICABLE"
return Matrix(matrix.tolist()) return Matrix(matrix.tolist())
if obj.data and obj.data.get("has_cartesian_point_offset", None): if obj.data and obj.data.get("has_cartesian_point_offset", None):
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT" oprops.blender_offset_type = "CARTESIAN_POINT"
if cartesian_point_offset := obj.data.get("cartesian_point_offset", None): if cartesian_point_offset := obj.data.get("cartesian_point_offset", None):
obj.BIMObjectProperties.cartesian_point_offset = cartesian_point_offset oprops.cartesian_point_offset = cartesian_point_offset
offset_xyz = list(map(float, cartesian_point_offset.split(","))) + [1.0] offset_xyz = list(map(float, cartesian_point_offset.split(","))) + [1.0]
offset_xyz = matrix @ offset_xyz offset_xyz = matrix @ offset_xyz
matrix[0][3] = offset_xyz[0] matrix[0][3] = offset_xyz[0]
@@ -954,8 +955,8 @@ class Loader(bonsai.core.tool.Loader):
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
if props.has_blender_offset: if props.has_blender_offset:
if obj.BIMObjectProperties.blender_offset_type == "NONE": if oprops.blender_offset_type == "NONE":
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT" oprops.blender_offset_type = "OBJECT_PLACEMENT"
matrix = ifcopenshell.util.geolocation.global2local( matrix = ifcopenshell.util.geolocation.global2local(
matrix, matrix,
float(props.blender_offset_x) * cls.unit_scale, float(props.blender_offset_x) * cls.unit_scale,
+31 -3
View File
@@ -55,7 +55,15 @@ T = TypeVar("T")
V_ = tool.Blender.V_ V_ = tool.Blender.V_
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.module.model.prop import BIMModelProperties, BIMDoorProperties from bonsai.bim.module.model.prop import (
BIMModelProperties,
BIMDoorProperties,
BIMArrayProperties,
BIMRoofProperties,
BIMWindowProperties,
BIMStairProperties,
BIMRailingProperties,
)
class Model(bonsai.core.tool.Model): class Model(bonsai.core.tool.Model):
@@ -67,6 +75,26 @@ class Model(bonsai.core.tool.Model):
def get_door_props(cls, obj: bpy.types.Object) -> BIMDoorProperties: def get_door_props(cls, obj: bpy.types.Object) -> BIMDoorProperties:
return obj.BIMDoorProperties return obj.BIMDoorProperties
@classmethod
def get_window_props(cls, obj: bpy.types.Object) -> BIMWindowProperties:
return obj.BIMWindowProperties
@classmethod
def get_stair_props(cls, obj: bpy.types.Object) -> BIMStairProperties:
return obj.BIMStairProperties
@classmethod
def get_roof_props(cls, obj: bpy.types.Object) -> BIMRoofProperties:
return obj.BIMRoofProperties
@classmethod
def get_railing_props(cls, obj: bpy.types.Object) -> BIMRailingProperties:
return obj.BIMRailingProperties
@classmethod
def get_array_props(cls, obj: bpy.types.Object) -> BIMArrayProperties:
return obj.BIMArrayProperties
@classmethod @classmethod
def convert_si_to_unit(cls, value: T) -> T: def convert_si_to_unit(cls, value: T) -> T:
if isinstance(value, (tuple, list)): if isinstance(value, (tuple, list)):
@@ -1989,9 +2017,9 @@ class Model(bonsai.core.tool.Model):
if z < 0 and y < 0: if z < 0 and y < 0:
y = abs(y) y = abs(y)
z = abs(z) z = abs(z)
if z < 0 and y >=0: if z < 0 and y >= 0:
vector = Vector((0, -1)) vector = Vector((0, -1))
x_angle = vector.angle_signed(Vector((y, z))) x_angle = vector.angle_signed(Vector((y, z)))
return x_angle return x_angle
+2 -1
View File
@@ -409,7 +409,8 @@ class Root(bonsai.core.tool.Root):
def set_object_name(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None: def set_object_name(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None:
name = tool.Loader.get_name(element) name = tool.Loader.get_name(element)
if obj.name != name: if obj.name != name:
obj.BIMObjectProperties.is_renaming = True props = tool.Blender.get_object_bim_props(obj)
props.is_renaming = True
obj.name = name # The handler will trigger, and reset is_renaming to False obj.name = name # The handler will trigger, and reset is_renaming to False
@classmethod @classmethod
+6 -5
View File
@@ -1158,7 +1158,7 @@ class Sequence(bonsai.core.tool.Sequence):
bpy.context.scene.frame_start = 1 bpy.context.scene.frame_start = 1
bpy.context.scene.frame_end = 2 bpy.context.scene.frame_end = 2
for obj in bpy.data.objects: for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id: if not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
continue continue
obj.color = (1.0, 1.0, 1.0, 1) obj.color = (1.0, 1.0, 1.0, 1)
obj.hide_viewport = False obj.hide_viewport = False
@@ -1317,7 +1317,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def clear_objects_animation(cls, include_blender_objects=True): def clear_objects_animation(cls, include_blender_objects=True):
for obj in bpy.data.objects: for obj in bpy.data.objects:
if not include_blender_objects and not obj.BIMObjectProperties.ifc_definition_id: if not include_blender_objects and not (ifc_id := tool.Blender.get_ifc_definition_id(obj)):
continue continue
cls.clear_object_animation(obj) cls.clear_object_animation(obj)
cls.clear_object_color(obj) cls.clear_object_color(obj)
@@ -1326,13 +1326,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def animate_objects(cls, settings, frames, animation_type=""): def animate_objects(cls, settings, frames, animation_type=""):
for obj in bpy.data.objects: for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id: element = tool.Ifc.get_entity(obj)
if not element:
continue continue
if tool.Ifc.get().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcSpace"): if element.is_a("IfcSpace"):
cls.hide_object(obj) cls.hide_object(obj)
continue continue
cls.earliest_frame = None cls.earliest_frame = None
product_frames = frames.get(obj.BIMObjectProperties.ifc_definition_id, []) product_frames = frames.get(element.id(), [])
for product_frame in product_frames: for product_frame in product_frames:
if product_frame["relationship"] == "input": if product_frame["relationship"] == "input":
cls.animate_input(obj, settings["start_frame"], product_frame, animation_type) cls.animate_input(obj, settings["start_frame"], product_frame, animation_type)
+2 -2
View File
@@ -1180,9 +1180,9 @@ class Spatial(bonsai.core.tool.Spatial):
SpatialDecompositionData.data["default_container"] = SpatialDecompositionData.default_container() SpatialDecompositionData.data["default_container"] = SpatialDecompositionData.default_container()
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection project_collection = tool.Blender.get_object_bim_props(project).collection
obj = tool.Ifc.get_object(container) obj = tool.Ifc.get_object(container)
if obj and (collection := obj.BIMObjectProperties.collection): if obj and (collection := tool.Blender.get_object_bim_props(obj).collection):
for layer_collection in bpy.context.view_layer.layer_collection.children: for layer_collection in bpy.context.view_layer.layer_collection.children:
if layer_collection.collection == project_collection: if layer_collection.collection == project_collection:
for layer_collection2 in layer_collection.children: for layer_collection2 in layer_collection.children:
+2 -1
View File
@@ -110,7 +110,8 @@ class Structural(bonsai.core.tool.Structural):
def get_product_or_active_object(cls, product: str) -> Union[bpy.types.Object, None]: def get_product_or_active_object(cls, product: str) -> Union[bpy.types.Object, None]:
product = bpy.data.objects.get(product) if product else bpy.context.active_object product = bpy.data.objects.get(product) if product else bpy.context.active_object
try: try:
if product.BIMObjectProperties.ifc_definition_id: props = tool.Blender.get_object_bim_props(product)
if props.ifc_definition_id:
return product return product
else: else:
return None return None
+1 -1
View File
@@ -33,7 +33,7 @@ class Surveyor(bonsai.core.tool.Surveyor):
M_TRANSLATION = (slice(0, 3), 3) M_TRANSLATION = (slice(0, 3), 3)
matrix = np.array(obj.matrix_world) matrix = np.array(obj.matrix_world)
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE": if props.has_blender_offset and tool.Blender.get_object_bim_props(obj).blender_offset_type != "NOT_APPLICABLE":
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
coordinate_offset = tool.Geometry.get_cartesian_point_offset(obj) coordinate_offset = tool.Geometry.get_cartesian_point_offset(obj)
if coordinate_offset is not None: if coordinate_offset is not None:
+1 -1
View File
@@ -189,7 +189,7 @@ class System(bonsai.core.tool.System):
container = ifcopenshell.util.element.get_container(element) container = ifcopenshell.util.element.get_container(element)
if container: if container:
collection = tool.Ifc.get_object(container).BIMObjectProperties.collection collection = tool.Blender.get_object_bim_props(tool.Ifc.get_object(container)).collection
ifc_importer.collections[container.GlobalId] = collection ifc_importer.collections[container.GlobalId] = collection
ifc_importer.place_objects_in_collections() ifc_importer.place_objects_in_collections()
+13 -13
View File
@@ -192,12 +192,12 @@ def the_object_name_does_not_exist(name):
def the_object_name_is_an_ifc_class(name, ifc_class): def the_object_name_is_an_ifc_class(name, ifc_class):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert element.is_a(ifc_class), f'Object "{name}" is an {element.is_a()}' assert element.is_a(ifc_class), f'Object "{name}" is an {element.is_a()}'
def the_object_name_is_not_an_ifc_element(name): def the_object_name_is_not_an_ifc_element(name):
id = the_object_name_exists(name).BIMObjectProperties.ifc_definition_id id = tool.Blender.get_ifc_definition_id(the_object_name_exists(name))
assert id == 0, f"The ID is {id}" assert id == 0, f"The ID is {id}"
@@ -229,7 +229,7 @@ def the_object_name_is_placed_in_the_collection_collection(name, collection):
def the_object_name_has_a_type_representation_of_context(name, type, context): def the_object_name_has_a_type_representation_of_context(name, type, context):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
context, subcontext, target_view = context.split("/") context, subcontext, target_view = context.split("/")
assert ifcopenshell.util.representation.get_representation( assert ifcopenshell.util.representation.get_representation(
element, context, subcontext or None, target_view or None element, context, subcontext or None, target_view or None
@@ -238,7 +238,7 @@ def the_object_name_has_a_type_representation_of_context(name, type, context):
def the_object_name_is_contained_in_container_name(name, container_name): def the_object_name_is_contained_in_container_name(name, container_name):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
container = ifcopenshell.util.element.get_container(element) container = ifcopenshell.util.element.get_container(element)
if not container: if not container:
assert False, f'Object "{name}" is not in any container' assert False, f'Object "{name}" is not in any container'
@@ -257,8 +257,8 @@ def i_delete_the_selected_objects():
def the_object_name1_and_name2_are_different_elements(name1, name2): def the_object_name1_and_name2_are_different_elements(name1, name2):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element1 = ifc.by_id(the_object_name_exists(name1).BIMObjectProperties.ifc_definition_id) element1 = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name1)))
element2 = ifc.by_id(the_object_name_exists(name2).BIMObjectProperties.ifc_definition_id) element2 = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name2)))
assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}" assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}"
@@ -284,7 +284,7 @@ def the_object_name1_has_no_boolean_difference_by_name2(name1, name2):
def the_object_name_is_voided_by_void(name, void): def the_object_name_is_voided_by_void(name, void):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
for rel in element.HasOpenings: for rel in element.HasOpenings:
if rel.RelatedOpeningElement.Name == void: if rel.RelatedOpeningElement.Name == void:
return True return True
@@ -293,7 +293,7 @@ def the_object_name_is_voided_by_void(name, void):
def the_object_name_is_not_voided_by_void(name, void): def the_object_name_is_not_voided_by_void(name, void):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
for rel in element.HasOpenings: for rel in element.HasOpenings:
if rel.RelatedOpeningElement.Name == void: if rel.RelatedOpeningElement.Name == void:
assert False, "A void was found" assert False, "A void was found"
@@ -301,21 +301,21 @@ def the_object_name_is_not_voided_by_void(name, void):
def the_object_name_is_not_voided(name): def the_object_name_is_not_voided(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
if any(element.HasOpenings): if any(element.HasOpenings):
assert False, "An opening was found" assert False, "An opening was found"
def the_object_name_is_not_a_void(name): def the_object_name_is_not_a_void(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
if any(element.VoidsElements): if any(element.VoidsElements):
assert False, "A void was found" assert False, "A void was found"
def the_void_name_is_filled_by_filling(name, filling): def the_void_name_is_filled_by_filling(name, filling):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
if any(rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings): if any(rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings):
return True return True
assert False, "No filling found" assert False, "No filling found"
@@ -323,14 +323,14 @@ def the_void_name_is_filled_by_filling(name, filling):
def the_void_name_is_not_filled_by_filling(name, filling): def the_void_name_is_not_filled_by_filling(name, filling):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
if any(rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings): if any(rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings):
assert False, "A filling was found" assert False, "A filling was found"
def the_object_name_is_not_a_filling(name): def the_object_name_is_not_a_filling(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
if any(element.FillsVoids): if any(element.FillsVoids):
assert False, "A filling was found" assert False, "A filling was found"
+19 -18
View File
@@ -718,8 +718,8 @@ def the_collection_exclude_status_is(name: str, exclude: str) -> None:
@then(parsers.parse('the object "{name1}" and "{name2}" are different elements')) @then(parsers.parse('the object "{name1}" and "{name2}" are different elements'))
def the_object_name1_and_name2_are_different_elements(name1, name2): def the_object_name1_and_name2_are_different_elements(name1, name2):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element1 = ifc.by_id(the_object_name_exists(name1).BIMObjectProperties.ifc_definition_id) element1 = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name1)))
element2 = ifc.by_id(the_object_name_exists(name2).BIMObjectProperties.ifc_definition_id) element2 = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name2)))
assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}" assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}"
@@ -732,7 +732,7 @@ def the_object_name_has_a_body_of_value(name, value):
@then(parsers.parse('the object "{name}" has a "{type}" representation of "{context}"')) @then(parsers.parse('the object "{name}" has a "{type}" representation of "{context}"'))
def the_object_name_has_a_representation_type_of_context(name, type, context): def the_object_name_has_a_representation_type_of_context(name, type, context):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
context, subcontext, target_view = context.split("/") context, subcontext, target_view = context.split("/")
rep = ifcopenshell.util.representation.get_representation(element, context, subcontext or None, target_view or None) rep = ifcopenshell.util.representation.get_representation(element, context, subcontext or None, target_view or None)
assert rep assert rep
@@ -808,7 +808,7 @@ def the_object_name_should_display_as_mode(name, mode):
@then(parsers.parse('the object "{name}" is voided by "{void}"')) @then(parsers.parse('the object "{name}" is voided by "{void}"'))
def the_object_name_is_voided_by_void(name, void): def the_object_name_is_voided_by_void(name, void):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert any((rel for rel in element.HasOpenings if rel.RelatedOpeningElement.Name == void)), "No void found" assert any((rel for rel in element.HasOpenings if rel.RelatedOpeningElement.Name == void)), "No void found"
@@ -824,7 +824,7 @@ def the_object_name_is_not_voided_by_void(name, void):
@then(parsers.parse('the object "{name}" is not voided')) @then(parsers.parse('the object "{name}" is not voided'))
def the_object_name_is_not_voided(name): def the_object_name_is_not_voided(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert not element.HasOpenings, "A void was found" assert not element.HasOpenings, "A void was found"
@@ -832,7 +832,7 @@ def the_object_name_is_not_voided(name):
def the_object_name_is_a_void(name): def the_object_name_is_a_void(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
obj = the_object_name_exists(name) obj = the_object_name_exists(name)
element = ifc.by_id(obj.BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(obj))
assert any((element.VoidsElements)), "No void was found" assert any((element.VoidsElements)), "No void was found"
@@ -872,14 +872,14 @@ def the_object_name_is_not_visible(name):
@then(parsers.parse('the object "{name}" is an "{ifc_class}"')) @then(parsers.parse('the object "{name}" is an "{ifc_class}"'))
def the_object_name_is_an_ifc_class(name, ifc_class): def the_object_name_is_an_ifc_class(name, ifc_class):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert element.is_a(ifc_class), f'Object "{name}" is an {element.is_a()}' assert element.is_a(ifc_class), f'Object "{name}" is an {element.is_a()}'
@then(parsers.parse('the object "{name}" is not an IFC element')) @then(parsers.parse('the object "{name}" is not an IFC element'))
def the_object_name_is_not_an_ifc_element(name): def the_object_name_is_not_an_ifc_element(name):
obj = the_object_name_exists(name) obj = the_object_name_exists(name)
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id ifc_definition_id = tool.Blender.get_ifc_definition_id(obj)
assert ifc_definition_id == 0, f"The object {obj} has an ID of {ifc_definition_id}" assert ifc_definition_id == 0, f"The object {obj} has an ID of {ifc_definition_id}"
@@ -897,14 +897,14 @@ def the_object_name_has_ifc_representation_data(name):
@then(parsers.parse('the material "{name}" is an IFC material')) @then(parsers.parse('the material "{name}" is an IFC material'))
def the_material_name_is_an_ifc_material(name): def the_material_name_is_an_ifc_material(name):
obj = the_material_name_exists(name) obj = the_material_name_exists(name)
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id ifc_definition_id = tool.Blender.get_ifc_definition_id(obj)
assert ifc_definition_id != 0, f"The material {obj} has no ID: {ifc_definition_id}" assert ifc_definition_id != 0, f"The material {obj} has no ID: {ifc_definition_id}"
@then(parsers.parse('the material "{name}" is not an IFC material')) @then(parsers.parse('the material "{name}" is not an IFC material'))
def the_material_name_is_not_an_ifc_material(name): def the_material_name_is_not_an_ifc_material(name):
obj = the_material_name_exists(name) obj = the_material_name_exists(name)
ifc_definition_id = obj.BIMObjectProperties.ifc_definition_id ifc_definition_id = tool.Blender.get_ifc_definition_id(obj)
assert ifc_definition_id == 0, f"The material {obj} has an ID of {ifc_definition_id}" assert ifc_definition_id == 0, f"The material {obj} has an ID of {ifc_definition_id}"
@@ -937,7 +937,7 @@ def the_object_name_has_number_vertices(name, number):
@then(parsers.parse('the void "{name}" is filled by "{filling}"')) @then(parsers.parse('the void "{name}" is filled by "{filling}"'))
def the_void_name_is_filled_by_filling(name, filling): def the_void_name_is_filled_by_filling(name, filling):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert any((rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings)), "No filling found" assert any((rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings)), "No filling found"
@@ -954,7 +954,7 @@ def the_void_name_is_not_filled_by_filling(name, filling):
@then(parsers.parse('the object "{name}" is not a filling')) @then(parsers.parse('the object "{name}" is not a filling'))
def the_object_name_is_not_a_filling(name): def the_object_name_is_not_a_filling(name):
ifc = tool.Ifc.get() ifc = tool.Ifc.get()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
assert not any(element.FillsVoids), "A filling was found" assert not any(element.FillsVoids), "A filling was found"
@@ -1010,8 +1010,9 @@ def prop_is_roughly_value(prop, value):
def the_object_name_has_a_cartesian_point_offset_of_offset(name: str, offset: str) -> None: def the_object_name_has_a_cartesian_point_offset_of_offset(name: str, offset: str) -> None:
offset = replace_variables(offset) offset = replace_variables(offset)
obj = the_object_name_exists(name) obj = the_object_name_exists(name)
assert obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT" props = tool.Blender.get_object_props(obj)
obj_offset = np.array(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(",")))) assert props.blender_offset_type == "CARTESIAN_POINT"
obj_offset = np.array(tuple(map(float, props.cartesian_point_offset.split(","))))
offset = np.array(tuple(map(float, offset.split(",")))) offset = np.array(tuple(map(float, offset.split(","))))
assert np.allclose(obj_offset, offset) assert np.allclose(obj_offset, offset)
@@ -1169,7 +1170,7 @@ def the_object_name_bottom_left_corner_is_at_location(name, location):
@then(parsers.parse('the object "{name}" is contained in "{container_name}"')) @then(parsers.parse('the object "{name}" is contained in "{container_name}"'))
def the_object_name_is_contained_in_container_name(name, container_name): def the_object_name_is_contained_in_container_name(name, container_name):
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
container = ifcopenshell.util.element.get_container(element) container = ifcopenshell.util.element.get_container(element)
if not container: if not container:
assert False, f'Object "{name}" is not in any container' assert False, f'Object "{name}" is not in any container'
@@ -1179,7 +1180,7 @@ def the_object_name_is_contained_in_container_name(name, container_name):
@then(parsers.parse('the object "{name}" is contained in object "{container_name}"')) @then(parsers.parse('the object "{name}" is contained in object "{container_name}"'))
def the_object_name_is_contained_in_object_container_name(name: str, container_name: str) -> None: def the_object_name_is_contained_in_object_container_name(name: str, container_name: str) -> None:
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
container = ifcopenshell.util.element.get_container(element) container = ifcopenshell.util.element.get_container(element)
if not container: if not container:
assert False, f'Object "{name}" is not in any container' assert False, f'Object "{name}" is not in any container'
@@ -1190,7 +1191,7 @@ def the_object_name_is_contained_in_object_container_name(name: str, container_n
@then(parsers.parse('the object "{name}" is aggregated by object "{aggregate_name}"')) @then(parsers.parse('the object "{name}" is aggregated by object "{aggregate_name}"'))
def the_object_name_is_aggregated_by_object_aggregate_name(name: str, aggregate_name: str) -> None: def the_object_name_is_aggregated_by_object_aggregate_name(name: str, aggregate_name: str) -> None:
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
aggregate = ifcopenshell.util.element.get_aggregate(element) aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate: if not aggregate:
assert False, f'Object "{name}" is not aggregated by any element' assert False, f'Object "{name}" is not aggregated by any element'
@@ -1201,7 +1202,7 @@ def the_object_name_is_aggregated_by_object_aggregate_name(name: str, aggregate_
@then(parsers.parse('the object "{name}" has no aggregate')) @then(parsers.parse('the object "{name}" has no aggregate'))
def the_object_name_has_no_aggregate(name: str) -> None: def the_object_name_has_no_aggregate(name: str) -> None:
ifc = an_ifc_file_exists() ifc = an_ifc_file_exists()
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) element = ifc.by_id(tool.Blender.get_ifc_definition_id(the_object_name_exists(name)))
aggregate = ifcopenshell.util.element.get_aggregate(element) aggregate = ifcopenshell.util.element.get_aggregate(element)
if aggregate: if aggregate:
assert False, f'Object "{name}" is aggregated by element "{aggregate}"' assert False, f'Object "{name}" is aggregated by element "{aggregate}"'
+3 -1
View File
@@ -19,6 +19,7 @@
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.util.element import ifcopenshell.util.element
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
@@ -167,7 +168,8 @@ class TestAssign(NewIfc):
tool.Ifc.link(building_element, building_obj) tool.Ifc.link(building_element, building_obj)
building_collection = bpy.data.collections.new("Foobar") building_collection = bpy.data.collections.new("Foobar")
bpy.context.scene.collection.children.link(building_collection) bpy.context.scene.collection.children.link(building_collection)
building_obj.BIMObjectProperties.collection = building_collection props = tool.Blender.get_object_bim_props(building_obj)
props.collection = building_collection
building_collection.objects.link(building_obj) building_collection.objects.link(building_obj)
ifcopenshell.api.aggregate.assign_object( ifcopenshell.api.aggregate.assign_object(
tool.Ifc.get(), tool.Ifc.get(),
+2 -1
View File
@@ -319,7 +319,8 @@ class TestGetDrawingCollection(NewFile):
collection = bpy.data.collections.new("Collection") collection = bpy.data.collections.new("Collection")
bpy.context.scene.collection.children.link(collection) bpy.context.scene.collection.children.link(collection)
collection.objects.link(obj) collection.objects.link(obj)
obj.BIMObjectProperties.collection = collection props = tool.Blender.get_object_bim_props(obj)
props.collection = collection
collection.BIMCollectionProperties.obj = obj collection.BIMCollectionProperties.obj = obj
element = ifc.createIfcAnnotation() element = ifc.createIfcAnnotation()
+11 -7
View File
@@ -170,22 +170,25 @@ class TestGetTextLiteral(NewFile):
class TestGetCartesianPointCoordinateOffset(NewFile): class TestGetCartesianPointCoordinateOffset(NewFile):
def test_run(self): def test_run(self):
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT" oprops = tool.Blender.get_object_bim_props(obj)
oprops.blender_offset_type = "CARTESIAN_POINT"
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
props.has_blender_offset = True props.has_blender_offset = True
obj.BIMObjectProperties.cartesian_point_offset = "1,2,3" oprops.cartesian_point_offset = "1,2,3"
assert np.allclose(subject.get_cartesian_point_offset(obj), np.array((1.0, 2.0, 3.0))) assert np.allclose(subject.get_cartesian_point_offset(obj), np.array((1.0, 2.0, 3.0)))
def test_get_null_if_not_a_cartesian_point_offset_type(self): def test_get_null_if_not_a_cartesian_point_offset_type(self):
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
props.has_blender_offset = True props.has_blender_offset = True
obj.BIMObjectProperties.cartesian_point_offset = "1,2,3" oprops = tool.Blender.get_object_bim_props(obj)
oprops.cartesian_point_offset = "1,2,3"
assert subject.get_cartesian_point_offset(obj) is None assert subject.get_cartesian_point_offset(obj) is None
def test_get_null_if_no_blender_offset(self): def test_get_null_if_no_blender_offset(self):
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT" oprops = tool.Blender.get_object_bim_props(obj)
oprops.blender_offset_type = "CARTESIAN_POINT"
props = tool.Georeference.get_georeference_props() props = tool.Georeference.get_georeference_props()
props.has_blender_offset = False props.has_blender_offset = False
assert subject.get_cartesian_point_offset(obj) is None assert subject.get_cartesian_point_offset(obj) is None
@@ -307,15 +310,16 @@ class TestRecordObjectMaterials(NewFile):
material.BIMStyleProperties.ifc_definition_id = style.id() material.BIMStyleProperties.ifc_definition_id = style.id()
obj.data.materials.append(material) obj.data.materials.append(material)
subject.record_object_materials(obj) subject.record_object_materials(obj)
assert tool.Geometry.get_mesh_props(obj).material_checksum == str([style.id()]) assert tool.Geometry.get_mesh_props(obj.data).material_checksum == str([style.id()])
class TestRecordObjectPosition(NewFile): class TestRecordObjectPosition(NewFile):
def test_run(self): def test_run(self):
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
props = tool.Blender.get_object_bim_props(obj)
subject.record_object_position(obj) subject.record_object_position(obj)
assert obj.BIMObjectProperties.location_checksum == repr(np.array(obj.matrix_world.translation).tobytes()) assert props.location_checksum == repr(np.array(obj.matrix_world.translation).tobytes())
assert obj.BIMObjectProperties.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3()).tobytes()) assert props.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3()).tobytes())
class TestRemoveConnection(NewFile): class TestRemoveConnection(NewFile):
+4 -2
View File
@@ -129,14 +129,16 @@ class TestGetEntity(test.bim.bootstrap.NewFile):
def test_attempting_without_a_file(self): def test_attempting_without_a_file(self):
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.ifc_definition_id = 1 props = tool.Blender.get_object_bim_props(obj)
props.ifc_definition_id = 1
assert subject.get_entity(obj) is None assert subject.get_entity(obj) is None
def test_attempting_to_get_an_invalidly_linked_object(self): def test_attempting_to_get_an_invalidly_linked_object(self):
ifc = ifcopenshell.file() ifc = ifcopenshell.file()
subject.set(ifc) subject.set(ifc)
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.ifc_definition_id = 1 props = tool.Blender.get_object_bim_props(obj)
props.ifc_definition_id = 1
assert subject.get_entity(obj) is None assert subject.get_entity(obj) is None
+9 -6
View File
@@ -383,23 +383,26 @@ class TestUsingArrays(NewFile):
bpy.ops.mesh.primitive_cube_add() bpy.ops.mesh.primitive_cube_add()
obj = bpy.context.active_object obj = bpy.context.active_object
assert obj
rprops = tool.Root.get_root_props() rprops = tool.Root.get_root_props()
rprops.ifc_product = "IfcElement" rprops.ifc_product = "IfcElement"
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="") bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
bpy.ops.bim.add_array() bpy.ops.bim.add_array()
bpy.ops.bim.enable_editing_array(item=0) bpy.ops.bim.enable_editing_array(item=0)
obj.BIMArrayProperties.count = 4 props = tool.Model.get_array_props(obj)
obj.BIMArrayProperties.x = 4 props.count = 4
obj.BIMArrayProperties.sync_children = sync_children props.x = 4
props.sync_children = sync_children
bpy.ops.bim.edit_array(item=0) bpy.ops.bim.edit_array(item=0)
if add_second_layer: if add_second_layer:
bpy.ops.bim.add_array() bpy.ops.bim.add_array()
bpy.ops.bim.enable_editing_array(item=1) bpy.ops.bim.enable_editing_array(item=1)
obj.BIMArrayProperties.count = 3 props = tool.Model.get_array_props(obj)
obj.BIMArrayProperties.y = 4 props.count = 3
obj.BIMArrayProperties.sync_children = sync_children props.y = 4
props.sync_children = sync_children
bpy.ops.bim.edit_array(item=1) bpy.ops.bim.edit_array(item=1)
def test_remove_array_last_to_first(self): def test_remove_array_last_to_first(self):
+2 -1
View File
@@ -54,6 +54,7 @@ class TestGetGlobalMatrix(test.bim.bootstrap.NewFile):
props.blender_x_axis_abscissa = "0" props.blender_x_axis_abscissa = "0"
props.blender_x_axis_ordinate = "1" props.blender_x_axis_ordinate = "1"
obj = bpy.data.objects.new("Object", None) obj = bpy.data.objects.new("Object", None)
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT" props = tool.Blender.get_object_bim_props(obj)
props.blender_offset_type = "OBJECT_PLACEMENT"
matrix = ifcopenshell.util.geolocation.local2global(np.array(obj.matrix_world), 1.0, 2.0, 3.0, 0.0, 1.0) matrix = ifcopenshell.util.geolocation.local2global(np.array(obj.matrix_world), 1.0, 2.0, 3.0, 0.0, 1.0)
assert (subject.get_absolute_matrix(obj) == matrix).all() assert (subject.get_absolute_matrix(obj) == matrix).all()
+1 -1
View File
@@ -240,7 +240,7 @@ class TestImportUnitAttributes(NewFile):
assert props.unit_attributes["UnitType"].enum_value == "ABSORBEDDOSEUNIT" assert props.unit_attributes["UnitType"].enum_value == "ABSORBEDDOSEUNIT"
assert props.unit_attributes["Prefix"].enum_value == "EXA" assert props.unit_attributes["Prefix"].enum_value == "EXA"
assert props.unit_attributes["Name"].enum_value == "AMPERE" assert props.unit_attributes["Name"].enum_value == "AMPERE"
assert props.unit_attributes["Dimensions"] is None assert "Dimensions" not in props.unit_attributes
class TestImportUnits(NewFile): class TestImportUnits(NewFile):
@@ -442,7 +442,7 @@ class Usecase:
panel_schema: list[list[int]] = self.settings["panel_schema"] panel_schema: list[list[int]] = self.settings["panel_schema"]
panels: list[dict[str, Any]] = self.settings["panel_properties"] panels: list[dict[str, Any]] = self.settings["panel_properties"]
accumulated_height = [0] * len(panel_schema[0]) accumulated_height: list[float] = [0] * len(panel_schema[0])
built_panels: list[int] = [] built_panels: list[int] = []
window_items: list[ifcopenshell.entity_instance] = [] window_items: list[ifcopenshell.entity_instance] = []
lining_items: list[ifcopenshell.entity_instance] = [] lining_items: list[ifcopenshell.entity_instance] = []
@@ -96,7 +96,8 @@ class Patcher:
angle_threshold = 0.3 angle_threshold = 0.3
for obj in bpy.data.objects: for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id or not obj.data: ifc_id = tool.Blender.get_ifc_definition_id(obj)
if not ifc_id or not obj.data:
continue continue
if not obj.data.polygons: if not obj.data.polygons:
continue continue