This commit is contained in:
Andrej730
2025-02-03 17:40:46 +05:00
parent ecd7282150
commit d4f44cb0a1
25 changed files with 165 additions and 74 deletions
@@ -78,7 +78,8 @@ class BoundaryDecorator:
unselected_edges = [] unselected_edges = []
unselected_tris = [] unselected_tris = []
for boundary in context.scene.BIMBoundaryProperties.boundaries: props = tool.Boundary.get_boundary_props()
for boundary in props.boundaries:
obj = boundary.obj obj = boundary.obj
if not obj or not obj.data: # A boundary may not have data if it has no connection geometry if not obj or not obj.data: # A boundary may not have data if it has no connection geometry
continue continue
@@ -354,7 +354,9 @@ class EnableEditingBoundary(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
bprops = context.active_object.BIMBoundaryProperties obj = context.active_object
assert obj
bprops = tool.Boundary.get_object_boundary_props(obj)
bprops.is_editing = True bprops.is_editing = True
boundary = tool.Ifc.get_entity(context.active_object) boundary = tool.Ifc.get_entity(context.active_object)
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items(): for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
@@ -373,7 +375,9 @@ class DisableEditingBoundary(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
bprops = context.active_object.BIMBoundaryProperties obj = context.active_object
assert obj
bprops = tool.Boundary.get_object_boundary_props(obj)
bprops.is_editing = False bprops.is_editing = False
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items(): for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
setattr(bprops, blender_property, None) setattr(bprops, blender_property, None)
@@ -386,8 +390,10 @@ class EditBoundaryAttributes(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
bprops = context.active_object.BIMBoundaryProperties obj = context.active_object
boundary = tool.Ifc.get_entity(context.active_object) assert obj
bprops = tool.Boundary.get_object_boundary_props(obj)
boundary = tool.Ifc.get_entity(obj)
attributes = dict() attributes = dict()
for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items(): for ifc_attribute, blender_property in EDITABLE_ATTRIBUTES.items():
obj = getattr(bprops, blender_property, None) obj = getattr(bprops, blender_property, None)
@@ -519,6 +525,7 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Boundary.get_boundary_props()
to_delete = set() to_delete = set()
spaces = set() spaces = set()
for obj in context.selected_objects: for obj in context.selected_objects:
@@ -538,7 +545,7 @@ class HideBoundaries(bpy.types.Operator, tool.Ifc.Operator):
for boundary, boundary_obj in to_delete: for boundary, boundary_obj in to_delete:
tool.Ifc.unlink(element=boundary) tool.Ifc.unlink(element=boundary)
bpy.data.objects.remove(boundary_obj) bpy.data.objects.remove(boundary_obj)
context.scene.BIMBoundaryProperties.boundaries.clear() props.boundaries.clear()
return {"FINISHED"} return {"FINISHED"}
@@ -549,7 +556,7 @@ class DecorateBoundaries(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMBoundaryProperties props = tool.Boundary.get_boundary_props()
# filter not decorated boundaries and add decorations for them # filter not decorated boundaries and add decorations for them
decorated_boundaries = set([i.obj for i in props.boundaries]) decorated_boundaries = set([i.obj for i in props.boundaries])
active_boundaries = set() active_boundaries = set()
+14 -3
View File
@@ -30,23 +30,24 @@ from bpy.props import (
CollectionProperty, CollectionProperty,
) )
import bonsai.tool as tool import bonsai.tool as tool
from typing import TYPE_CHECKING, Union
def space_filter(self, object): def space_filter(self: "BIMObjectBoundaryProperties", object: bpy.types.Object) -> bool:
entity = tool.Ifc.get_entity(object) entity = tool.Ifc.get_entity(object)
if entity: if entity:
return entity.is_a("IfcSpace") or entity.is_a("IfcExternalSpatialElement") return entity.is_a("IfcSpace") or entity.is_a("IfcExternalSpatialElement")
return False return False
def boundary_filter(self, object): def boundary_filter(self: "BIMObjectBoundaryProperties", object: bpy.types.Object) -> bool:
entity = tool.Ifc.get_entity(object) entity = tool.Ifc.get_entity(object)
if entity: if entity:
return entity.is_a("IfcRelSpaceBoundary") return entity.is_a("IfcRelSpaceBoundary")
return False return False
def element_filter(self, object): def element_filter(self: "BIMObjectBoundaryProperties", object: bpy.types.Object) -> bool:
entity = tool.Ifc.get_entity(object) entity = tool.Ifc.get_entity(object)
if entity: if entity:
return entity.is_a("IfcElement") return entity.is_a("IfcElement")
@@ -60,6 +61,16 @@ class BIMObjectBoundaryProperties(PropertyGroup):
parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter) parent_boundary: PointerProperty(name="ParentBoundary", type=bpy.types.Object, poll=boundary_filter)
corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter) corresponding_boundary: PointerProperty(name="CorrespondingBoundary", type=bpy.types.Object, poll=boundary_filter)
if TYPE_CHECKING:
is_editing: bool
relating_space: Union[bpy.types.Object, None]
related_building_element: Union[bpy.types.Object, None]
parent_boundary: Union[bpy.types.Object, None]
corresponding_boundary: Union[bpy.types.Object, None]
class BIMBoundaryProperties(PropertyGroup): class BIMBoundaryProperties(PropertyGroup):
boundaries: bpy.props.CollectionProperty(type=ObjProperty) boundaries: bpy.props.CollectionProperty(type=ObjProperty)
if TYPE_CHECKING:
boundaries: bpy.types.bpy_prop_collection_idprop[ObjProperty]
+4 -2
View File
@@ -64,10 +64,12 @@ class BIM_PT_Boundary(Panel):
return entity.is_a("IfcRelSpaceBoundary") return entity.is_a("IfcRelSpaceBoundary")
def draw(self, context): def draw(self, context):
props = context.active_object.BIMObjectProperties obj = context.active_object
assert obj
props = obj.BIMObjectProperties
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 = context.active_object.BIMBoundaryProperties self.bprops = tool.Boundary.get_object_boundary_props(obj)
if self.bprops.is_editing: if self.bprops.is_editing:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.operator("bim.edit_boundary_attributes", icon="CHECKMARK", text="Save Attributes") row.operator("bim.edit_boundary_attributes", icon="CHECKMARK", text="Save Attributes")
+3 -2
View File
@@ -325,10 +325,11 @@ class CostItemQuantitiesData:
@classmethod @classmethod
def process_quantity_names(cls): def process_quantity_names(cls):
active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index
total_tasks = len(bpy.context.scene.BIMTaskTreeProperties.tasks) tprops = tool.Sequence.get_task_tree_props()
total_tasks = len(tprops.tasks)
if not total_tasks or active_task_index >= total_tasks: if not total_tasks or active_task_index >= total_tasks:
return [] return []
ifc_definition_id = bpy.context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id ifc_definition_id = tprops.tasks[active_task_index].ifc_definition_id
element = tool.Ifc.get().by_id(ifc_definition_id) element = tool.Ifc.get().by_id(ifc_definition_id)
names = set() names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
+2 -1
View File
@@ -19,6 +19,7 @@
import bpy import bpy
import bonsai.bim.helper import bonsai.bim.helper
import bonsai.bim.module.cost.prop as CostProp import bonsai.bim.module.cost.prop as CostProp
import bonsai.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from bonsai.bim.module.cost.data import CostSchedulesData from bonsai.bim.module.cost.data import CostSchedulesData
@@ -461,7 +462,7 @@ class BIM_PT_cost_item_quantities(Panel):
total_cost_item_processes = len(self.props.cost_item_processes) total_cost_item_processes = len(self.props.cost_item_processes)
row2.label(text="Tasks ({})".format(total_cost_item_processes)) row2.label(text="Tasks ({})".format(total_cost_item_processes))
tprops = context.scene.BIMTaskTreeProperties tprops = tool.Sequence.get_task_tree_props()
wprops = context.scene.BIMWorkScheduleProperties wprops = context.scene.BIMWorkScheduleProperties
if tprops.tasks and wprops.active_task_index < len(tprops.tasks): if tprops.tasks and wprops.active_task_index < len(tprops.tasks):
if has_quantity_names: if has_quantity_names:
@@ -422,7 +422,7 @@ class CreateDrawing(bpy.types.Operator):
self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save() self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save()
return svg_path return svg_path
def get_linework_contexts(self, ifc, target_view) -> LineworkContexts: def get_linework_contexts(self, ifc: ifcopenshell.file, target_view: str) -> LineworkContexts:
plan_body_target_contexts = [] plan_body_target_contexts = []
plan_body_model_contexts = [] plan_body_model_contexts = []
model_body_target_contexts = [] model_body_target_contexts = []
@@ -42,6 +42,7 @@ import ifcopenshell.util.representation
import ifcopenshell.util.shape import ifcopenshell.util.shape
import ifcopenshell.util.unit import ifcopenshell.util.unit
import bonsai.bim.handler import bonsai.bim.handler
import bonsai.bim.helper
import bonsai.bim.schema import bonsai.bim.schema
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.core.project as core import bonsai.core.project as core
+6 -6
View File
@@ -128,7 +128,7 @@ class BIM_PT_project(Panel):
self.layout.use_property_decorate = False self.layout.use_property_decorate = False
self.layout.use_property_split = True self.layout.use_property_split = True
props = context.scene.BIMProperties props = context.scene.BIMProperties
pprops = context.scene.BIMProjectProperties pprops = self.props = tool.Project.get_project_props()
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
if pprops.is_loading: if pprops.is_loading:
self.draw_advanced_loading_ui(context) self.draw_advanced_loading_ui(context)
@@ -148,7 +148,7 @@ class BIM_PT_project(Panel):
self.draw_unsaved_project_ui(context) self.draw_unsaved_project_ui(context)
def draw_advanced_loading_ui(self, context): def draw_advanced_loading_ui(self, context):
pprops = context.scene.BIMProjectProperties pprops = self.props
prop_with_search(self.layout, pprops, "filter_mode") prop_with_search(self.layout, pprops, "filter_mode")
if pprops.filter_mode in ["DECOMPOSITION", "IFC_CLASS", "IFC_TYPE"]: if pprops.filter_mode in ["DECOMPOSITION", "IFC_CLASS", "IFC_TYPE"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -222,7 +222,7 @@ class BIM_PT_project(Panel):
row.operator("bim.load_project_elements") row.operator("bim.load_project_elements")
def draw_editing_buttons(self, context, row): def draw_editing_buttons(self, context, row):
pprops = context.scene.BIMProjectProperties pprops = self.props
if IfcStore.get_file(): if IfcStore.get_file():
if pprops.is_editing: if pprops.is_editing:
row.operator("bim.edit_header", icon="CHECKMARK", text="") row.operator("bim.edit_header", icon="CHECKMARK", text="")
@@ -231,7 +231,7 @@ class BIM_PT_project(Panel):
row.operator("bim.enable_editing_header", icon="GREASEPENCIL", text="") row.operator("bim.enable_editing_header", icon="GREASEPENCIL", text="")
def draw_editable_file_info(self, context): def draw_editable_file_info(self, context):
pprops = context.scene.BIMProjectProperties pprops = self.props
if IfcStore.get_file(): if IfcStore.get_file():
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -316,7 +316,7 @@ class BIM_PT_new_project_wizard(Panel):
self.layout.use_property_split = True self.layout.use_property_split = True
props = context.scene.BIMProperties props = context.scene.BIMProperties
pprops = context.scene.BIMProjectProperties pprops = tool.Project.get_project_props()
prop_with_search(self.layout, pprops, "export_schema") prop_with_search(self.layout, pprops, "export_schema")
row = self.layout.row() row = self.layout.row()
row.prop(context.scene.unit_settings, "system") row.prop(context.scene.unit_settings, "system")
@@ -418,7 +418,7 @@ class BIM_PT_links(Panel):
bl_parent_id = "BIM_PT_tab_project_setup" bl_parent_id = "BIM_PT_tab_project_setup"
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMProjectProperties self.props = tool.Project.get_project_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.operator("bim.link_ifc") row.operator("bim.link_ifc")
if self.props.links: if self.props.links:
+6 -2
View File
@@ -55,7 +55,11 @@ class Data:
for name, data in sorted(psetqtos.items()): for name, data in sorted(psetqtos.items()):
pset = ifc_file.by_id(data["id"]) pset = ifc_file.by_id(data["id"])
pset_uses = ifcopenshell.util.element.get_elements_by_pset(pset) pset_uses = ifcopenshell.util.element.get_elements_by_pset(pset)
has_template = bool(tool.Pset.get_pset_template(name)) pset_template = tool.Pset.get_pset_template(name)
if has_template := bool(pset_template):
template_available_in_ui = pset_template
else:
template_available_in_ui = False
results.append( results.append(
{ {
"id": data["id"], "id": data["id"],
@@ -214,7 +218,7 @@ class TaskQtosData(Data):
@classmethod @classmethod
def load(cls): def load(cls):
wprops = bpy.context.scene.BIMWorkScheduleProperties wprops = bpy.context.scene.BIMWorkScheduleProperties
tprops = bpy.context.scene.BIMTaskTreeProperties tprops = tool.Sequence.get_task_tree_props()
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)} cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)}
cls.is_loaded = True cls.is_loaded = True
@@ -491,14 +491,14 @@ class SavePsetAsTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOpera
pset_id: bpy.props.IntProperty() pset_id: bpy.props.IntProperty()
def invoke(self, context, event): def invoke(self, context, event):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
if tool.Blender.get_enum_safe(props, "pset_template_files") is None: if tool.Blender.get_enum_safe(props, "pset_template_files") is None:
self.report({"ERROR"}, "No template files found. You can create one in Property Set Templates UI.") self.report({"ERROR"}, "No template files found. You can create one in Property Set Templates UI.")
return {"CANCELLED"} return {"CANCELLED"}
return context.window_manager.invoke_props_dialog(self, width=250) return context.window_manager.invoke_props_dialog(self, width=250)
def draw(self, context): def draw(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
self.layout.prop(props, "pset_template_files", text="Template File") self.layout.prop(props, "pset_template_files", text="Template File")
def _execute(self, context): def _execute(self, context):
+6 -7
View File
@@ -40,7 +40,7 @@ from typing import Any, Optional, TYPE_CHECKING
from typing_extensions import assert_never from typing_extensions import assert_never
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.module.pset.prop import IfcProperty from bonsai.bim.module.pset.prop import IfcProperty, PsetProperties
def draw_property(prop: IfcProperty, layout: bpy.types.UILayout, copy_operator: Optional[str] = None) -> None: def draw_property(prop: IfcProperty, layout: bpy.types.UILayout, copy_operator: Optional[str] = None) -> None:
@@ -73,7 +73,7 @@ def draw_single_property(prop: IfcProperty, layout: bpy.types.UILayout, copy_ope
def draw_enumerated_property( def draw_enumerated_property(
prop: bpy.types.PropertyGroup, layout: bpy.types.UILayout, copy_operator: Optional[str] = None prop: IfcProperty, layout: bpy.types.UILayout, copy_operator: Optional[str] = None
) -> None: ) -> None:
value_name = prop.metadata.get_value_name() value_name = prop.metadata.get_value_name()
if not value_name: if not value_name:
@@ -99,7 +99,7 @@ def draw_psetqto_ui(
context: bpy.types.Context, context: bpy.types.Context,
pset_id: int, pset_id: int,
pset: dict[str, Any], pset: dict[str, Any],
props: bpy.types.PropertyGroup, props: PsetProperties,
layout: bpy.types.UILayout, layout: bpy.types.UILayout,
obj_type: tool.Ifc.OBJECT_TYPE, obj_type: tool.Ifc.OBJECT_TYPE,
allow_removing: bool = True, allow_removing: bool = True,
@@ -219,9 +219,7 @@ def draw_psetqto_ui(
row.label(text="No Properties") row.label(text="No Properties")
def draw_psetqto_editable_ui( def draw_psetqto_editable_ui(box: bpy.types.UILayout, props: PsetProperties, prop: IfcProperty) -> None:
box: bpy.types.UILayout, props: bpy.types.PropertyGroup, prop: bpy.types.PropertyGroup
) -> None:
row = box.row(align=True) row = box.row(align=True)
draw_property(prop, row, copy_operator="bim.copy_property_to_selection") draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
@@ -493,7 +491,8 @@ class BIM_PT_task_qtos(Panel):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
if not props.active_work_schedule_id: if not props.active_work_schedule_id:
return False return False
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks) tprops = tool.Sequence.get_task_tree_props()
total_tasks = len(tprops.tasks)
if total_tasks > 0 and props.active_task_index < total_tasks: if total_tasks > 0 and props.active_task_index < total_tasks:
return True return True
return False return False
@@ -126,13 +126,14 @@ class PsetTemplatesData:
if not cls.data["pset_template_files"]: if not cls.data["pset_template_files"]:
return [] return []
if not IfcStore.pset_template_file: if not IfcStore.pset_template_file:
IfcStore.pset_template_path = bpy.context.scene.BIMPsetTemplateProperties.pset_template_files props = tool.PsetTemplate.get_pset_template_props()
IfcStore.pset_template_path = props.pset_template_files
IfcStore.pset_template_file = ifcopenshell.open(IfcStore.pset_template_path) IfcStore.pset_template_file = ifcopenshell.open(IfcStore.pset_template_path)
return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")] return [(str(t.id()), t.Name, "") for t in IfcStore.pset_template_file.by_type("IfcPropertySetTemplate")]
@classmethod @classmethod
def pset_template(cls) -> dict[str, Any]: def pset_template(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
template_id = props.pset_templates template_id = props.pset_templates
if not template_id: if not template_id:
return {} return {}
@@ -145,7 +146,7 @@ class PsetTemplatesData:
@classmethod @classmethod
def prop_templates(cls) -> list[dict[str, Any]]: def prop_templates(cls) -> list[dict[str, Any]]:
props = bpy.context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
template_id = props.pset_templates template_id = props.pset_templates
if not template_id: if not template_id:
return [] return []
@@ -49,7 +49,8 @@ class AddPsetTemplateFile(bpy.types.Operator):
template.write(filepath) template.write(filepath)
bonsai.bim.handler.refresh_ui_data() bonsai.bim.handler.refresh_ui_data()
bonsai.bim.schema.reload(tool.Ifc.get().schema) bonsai.bim.schema.reload(tool.Ifc.get().schema)
context.scene.BIMPsetTemplateProperties.pset_template_files = filepath props = tool.PsetTemplate.get_pset_template_props()
props.pset_template_files = filepath
tool.PsetTemplate.enable_editing_pset_template() tool.PsetTemplate.enable_editing_pset_template()
return {"FINISHED"} return {"FINISHED"}
@@ -67,7 +68,8 @@ class AddPsetTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperator
self.template_file.write(IfcStore.pset_template_path) self.template_file.write(IfcStore.pset_template_path)
bonsai.bim.handler.refresh_ui_data() bonsai.bim.handler.refresh_ui_data()
bonsai.bim.schema.reload(tool.Ifc.get().schema) bonsai.bim.schema.reload(tool.Ifc.get().schema)
context.scene.BIMPsetTemplateProperties.pset_templates = str(template.id()) props = tool.PsetTemplate.get_pset_template_props()
props.pset_templates = str(template.id())
class RemovePsetTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperator): class RemovePsetTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperator):
@@ -76,7 +78,7 @@ class RemovePsetTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOpera
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
current_pset_template_id = int(props.pset_templates) current_pset_template_id = int(props.pset_templates)
if props.active_pset_template_id == current_pset_template_id: if props.active_pset_template_id == current_pset_template_id:
bpy.ops.bim.disable_editing_pset_template() bpy.ops.bim.disable_editing_pset_template()
@@ -107,7 +109,7 @@ class DisableEditingPsetTemplate(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
props.active_pset_template_id = 0 props.active_pset_template_id = 0
return {"FINISHED"} return {"FINISHED"}
@@ -130,7 +132,8 @@ class DeletePropEnum(bpy.types.Operator):
index: bpy.props.IntProperty() index: bpy.props.IntProperty()
def execute(self, context): def execute(self, context):
active_prop = context.scene.BIMPsetTemplateProperties.active_prop_template props = tool.PsetTemplate.get_pset_template_props()
active_prop = props.active_prop_template
active_prop.enum_values.remove(self.index) active_prop.enum_values.remove(self.index)
return {"FINISHED"} return {"FINISHED"}
@@ -142,7 +145,8 @@ class AddPropEnum(bpy.types.Operator):
index: bpy.props.IntProperty() index: bpy.props.IntProperty()
def execute(self, context): def execute(self, context):
active_prop = context.scene.BIMPsetTemplateProperties.active_prop_template props = tool.PsetTemplate.get_pset_template_props()
active_prop = props.active_prop_template
active_prop.enum_values.add() active_prop.enum_values.add()
return {"FINISHED"} return {"FINISHED"}
@@ -153,7 +157,7 @@ class DisableEditingPropTemplate(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
props.active_prop_template_id = 0 props.active_prop_template_id = 0
return {"FINISHED"} return {"FINISHED"}
@@ -164,7 +168,7 @@ class EditPsetTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperato
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
ifcopenshell.api.run( ifcopenshell.api.run(
"pset_template.edit_pset_template", "pset_template.edit_pset_template",
IfcStore.pset_template_file, IfcStore.pset_template_file,
@@ -209,7 +213,7 @@ class RemovePsetTemplateFile(bpy.types.Operator):
bonsai.bim.schema.reload(tool.Ifc.get().schema) bonsai.bim.schema.reload(tool.Ifc.get().schema)
# Ensure enum is valid after deletion. # Ensure enum is valid after deletion.
self.props = context.scene.BIMPsetTemplateProperties self.props = tool.PsetTemplate.get_pset_template_props()
if not tool.Blender.ensure_enum_is_valid(self.props, "pset_template_files"): if not tool.Blender.ensure_enum_is_valid(self.props, "pset_template_files"):
self.update_template_files_prop(context) self.update_template_files_prop(context)
return {"FINISHED"} return {"FINISHED"}
@@ -226,7 +230,7 @@ class AddPropTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperator
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
pset_template_id = props.active_pset_template_id or int(props.pset_templates) pset_template_id = props.active_pset_template_id or int(props.pset_templates)
prop_template = ifcopenshell.api.run( prop_template = ifcopenshell.api.run(
"pset_template.add_prop_template", "pset_template.add_prop_template",
@@ -264,7 +268,7 @@ class EditPropTemplate(bpy.types.Operator, tool.PsetTemplate.PsetTemplateOperato
def _execute(self, context): def _execute(self, context):
assert IfcStore.pset_template_file assert IfcStore.pset_template_file
props = context.scene.BIMPsetTemplateProperties props = tool.PsetTemplate.get_pset_template_props()
active_prop_template = props.active_prop_template active_prop_template = props.active_prop_template
if props.active_prop_template.template_type == "P_ENUMERATEDVALUE": if props.active_prop_template.template_type == "P_ENUMERATEDVALUE":
data_type = props.active_prop_template.get_value_name() data_type = props.active_prop_template.get_value_name()
@@ -36,7 +36,7 @@ class BIM_PT_pset_template(Panel):
if not PsetTemplatesData.is_loaded: if not PsetTemplatesData.is_loaded:
PsetTemplatesData.load() PsetTemplatesData.load()
self.props = context.scene.BIMPsetTemplateProperties self.props = tool.PsetTemplate.get_pset_template_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
if PsetTemplatesData.data["pset_template_files"]: if PsetTemplatesData.data["pset_template_files"]:
@@ -40,6 +40,7 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
from typing import TYPE_CHECKING
def getTaskColumns(self, context): def getTaskColumns(self, context):
@@ -343,6 +344,26 @@ class Task(PropertyGroup):
is_predecessor: BoolProperty(name="Is Predecessor") is_predecessor: BoolProperty(name="Is Predecessor")
is_successor: BoolProperty(name="Is Successor") is_successor: BoolProperty(name="Is Successor")
if TYPE_CHECKING:
name: str
identification: str
ifc_definition_id: int
has_children: bool
is_selected: bool
is_expanded: bool
has_bar_visual: bool
level_index: int
duration: str
start: str
finish: str
calendar: str
derived_start: str
derived_finish: str
derived_duration: str
derived_calendar: str
is_predecessor: bool
is_successor: bool
class WorkPlan(PropertyGroup): class WorkPlan(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -479,6 +500,9 @@ class BIMTaskTreeProperties(PropertyGroup):
# In Blender if you add many collection items it makes other property access in the same group really slow. # In Blender if you add many collection items it makes other property access in the same group really slow.
tasks: CollectionProperty(name="Tasks", type=Task) tasks: CollectionProperty(name="Tasks", type=Task)
if TYPE_CHECKING:
tasks: bpy.types.bpy_prop_collection_idprop[Task]
class WorkCalendar(PropertyGroup): class WorkCalendar(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
+4 -3
View File
@@ -156,7 +156,7 @@ class BIM_PT_work_schedules(Panel):
if not WorkScheduleData.is_loaded: if not WorkScheduleData.is_loaded:
WorkScheduleData.load() WorkScheduleData.load()
self.props = context.scene.BIMWorkScheduleProperties self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties self.tprops = tool.Sequence.get_task_tree_props()
if not self.props.active_work_schedule_id: if not self.props.active_work_schedule_id:
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -649,7 +649,8 @@ class BIM_PT_task_icom(Panel):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
if not props.active_work_schedule_id: if not props.active_work_schedule_id:
return False return False
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks) tprops = tool.Sequence.get_task_tree_props()
total_tasks = len(tprops.tasks)
if total_tasks > 0 and props.active_task_index < total_tasks: if total_tasks > 0 and props.active_task_index < total_tasks:
return True return True
return False return False
@@ -659,7 +660,7 @@ class BIM_PT_task_icom(Panel):
TaskICOMData.load() TaskICOMData.load()
self.props = context.scene.BIMWorkScheduleProperties self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties self.tprops = tool.Sequence.get_task_tree_props()
task = self.tprops.tasks[self.props.active_task_index] task = self.tprops.tasks[self.props.active_task_index]
grid = self.layout.grid_flow(columns=3, even_columns=True) grid = self.layout.grid_flow(columns=3, even_columns=True)
+7
View File
@@ -169,6 +169,9 @@ class StrProperty(PropertyGroup):
class ObjProperty(PropertyGroup): class ObjProperty(PropertyGroup):
obj: bpy.props.PointerProperty(type=bpy.types.Object) obj: bpy.props.PointerProperty(type=bpy.types.Object)
if TYPE_CHECKING:
obj: Union[bpy.types.Object, None]
def update_single_file(self: "MultipleFileSelect", context: bpy.types.Context) -> None: def update_single_file(self: "MultipleFileSelect", context: bpy.types.Context) -> None:
self.file_list.clear() self.file_list.clear()
@@ -180,6 +183,10 @@ class MultipleFileSelect(PropertyGroup):
single_file: bpy.props.StringProperty(name="Single File Path", description="", update=update_single_file) single_file: bpy.props.StringProperty(name="Single File Path", description="", update=update_single_file)
file_list: bpy.props.CollectionProperty(type=StrProperty) file_list: bpy.props.CollectionProperty(type=StrProperty)
if TYPE_CHECKING:
single_file: str
file_list: bpy.types.bpy_prop_collection_idprop[StrProperty]
def set_file_list(self, dirname: str, files: list[str]) -> None: def set_file_list(self, dirname: str, files: list[str]) -> None:
self.file_list.clear() self.file_list.clear()
+2 -3
View File
@@ -194,9 +194,8 @@ class Blender(bonsai.core.tool.Blender):
elif obj_type == "MaterialSetItem": elif obj_type == "MaterialSetItem":
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
elif obj_type == "Task": elif obj_type == "Task":
return context.scene.BIMTaskTreeProperties.tasks[ tprops = tool.Sequence.get_task_tree_props()
context.scene.BIMWorkScheduleProperties.active_task_index return tprops.tasks[context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id
].ifc_definition_id
elif obj_type == "Cost": elif obj_type == "Cost":
return context.scene.BIMCostProperties.cost_items[ return context.scene.BIMCostProperties.cost_items[
context.scene.BIMCostProperties.active_cost_item_index context.scene.BIMCostProperties.active_cost_item_index
+15 -2
View File
@@ -16,15 +16,27 @@
# 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 mathutils import mathutils
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
from mathutils import Matrix, Vector from mathutils import Matrix, Vector
from typing import Any, Sequence from typing import Any, Sequence, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.boundary.prop import BIMBoundaryProperties, BIMObjectBoundaryProperties
class Boundary(bonsai.core.tool.Boundary): class Boundary(bonsai.core.tool.Boundary):
@classmethod
def get_boundary_props(cls) -> BIMBoundaryProperties:
return bpy.context.scene.BIMBoundaryProperties
@classmethod
def get_object_boundary_props(cls, obj: bpy.types.Object) -> BIMObjectBoundaryProperties:
return obj.BIMBoundaryProperties
@classmethod @classmethod
def get_assign_connection_geometry_settings(cls, obj: bpy.types.Object) -> dict[str, Any]: def get_assign_connection_geometry_settings(cls, obj: bpy.types.Object) -> dict[str, Any]:
from bonsai.bim.module.geometry.helper import Helper from bonsai.bim.module.geometry.helper import Helper
@@ -76,7 +88,8 @@ class Boundary(bonsai.core.tool.Boundary):
@classmethod @classmethod
def decorate_boundary(cls, obj: bpy.types.Object) -> None: def decorate_boundary(cls, obj: bpy.types.Object) -> None:
new = bpy.context.scene.BIMBoundaryProperties.boundaries.add() props = cls.get_boundary_props()
new = props.boundaries.add()
new.obj = obj new.obj = obj
obj.show_in_front = True obj.show_in_front = True
+1 -1
View File
@@ -135,7 +135,7 @@ class Pset(bonsai.core.tool.Pset):
return special_type return special_type
@classmethod @classmethod
def import_pset_from_existing(cls, pset: ifcopenshell.entity_instance, props: bpy.types.PropertyGroup) -> None: def import_pset_from_existing(cls, pset: ifcopenshell.entity_instance, props: PsetProperties) -> None:
pset_props = [] pset_props = []
if pset.is_a("IfcElementQuantity"): if pset.is_a("IfcElementQuantity"):
pset_props = pset.Quantities pset_props = pset.Quantities
+16 -10
View File
@@ -42,12 +42,17 @@ from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable
if TYPE_CHECKING: if TYPE_CHECKING:
import bonsai.bim.prop import bonsai.bim.prop
from bonsai.bim.module.sequence.prop import BIMTaskTreeProperties
class Sequence(bonsai.core.tool.Sequence): class Sequence(bonsai.core.tool.Sequence):
RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"] RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"]
@classmethod
def get_task_tree_props(cls) -> BIMTaskTreeProperties:
return bpy.context.scene.BIMTaskTreeProperties
@classmethod @classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]: def get_work_plan_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper import bonsai.bim.module.sequence.helper as helper
@@ -146,7 +151,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None: def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMTaskTreeProperties.tasks.clear() props = cls.get_task_tree_props()
props.tasks.clear()
props = bpy.context.scene.BIMWorkScheduleProperties props = bpy.context.scene.BIMWorkScheduleProperties
cls.contracted_tasks = json.loads(props.contracted_tasks) cls.contracted_tasks = json.loads(props.contracted_tasks)
@@ -185,7 +191,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def create_new_task_li(cls, related_object_id: int, level_index: int) -> None: def create_new_task_li(cls, related_object_id: int, level_index: int) -> None:
task = tool.Ifc.get().by_id(related_object_id) task = tool.Ifc.get().by_id(related_object_id)
new = bpy.context.scene.BIMTaskTreeProperties.tasks.add() props = cls.get_task_tree_props()
new = props.tasks.add()
new.ifc_definition_id = related_object_id new.ifc_definition_id = related_object_id
new.is_expanded = related_object_id not in cls.contracted_tasks new.is_expanded = related_object_id not in cls.contracted_tasks
new.level_index = level_index new.level_index = level_index
@@ -199,7 +206,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None: def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = bpy.context.scene.BIMWorkScheduleProperties
task_props = bpy.context.scene.BIMTaskTreeProperties task_props = cls.get_task_tree_props()
tasks_with_visual_bar = cls.get_task_bar_list() tasks_with_visual_bar = cls.get_task_bar_list()
props.is_task_update_enabled = False props.is_task_update_enabled = False
@@ -281,8 +288,9 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def contract_all_tasks(cls) -> None: def contract_all_tasks(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = bpy.context.scene.BIMWorkScheduleProperties
tprops = cls.get_task_tree_props()
contracted_tasks = json.loads(props.contracted_tasks) contracted_tasks = json.loads(props.contracted_tasks)
for task_item in bpy.context.scene.BIMTaskTreeProperties.tasks: for task_item in tprops.tasks:
if task_item.is_expanded: if task_item.is_expanded:
contracted_tasks.append(task_item.ifc_definition_id) contracted_tasks.append(task_item.ifc_definition_id)
props.contracted_tasks = json.dumps(contracted_tasks) props.contracted_tasks = json.dumps(contracted_tasks)
@@ -302,7 +310,7 @@ class Sequence(bonsai.core.tool.Sequence):
def disable_selecting_deleted_task(cls) -> None: def disable_selecting_deleted_task(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = bpy.context.scene.BIMWorkScheduleProperties
if props.active_task_id not in [ if props.active_task_id not in [
task.ifc_definition_id for task in bpy.context.scene.BIMTaskTreeProperties.tasks task.ifc_definition_id for task in cls.get_task_tree_props().tasks
]: # Task was deleted ]: # Task was deleted
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0 bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
@@ -310,9 +318,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]: def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]:
return [ return [
tool.Ifc.get().by_id(task.ifc_definition_id) tool.Ifc.get().by_id(task.ifc_definition_id) for task in cls.get_task_tree_props().tasks if task.is_selected
for task in bpy.context.scene.BIMTaskTreeProperties.tasks
if task.is_selected
] or [] ] or []
@classmethod @classmethod
@@ -486,7 +492,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]: def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]:
tasks = bpy.context.scene.BIMTaskTreeProperties.tasks tasks = cls.get_task_tree_props().tasks
if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index: if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index:
return tool.Ifc.get().by_id( return tool.Ifc.get().by_id(
tasks[bpy.context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id tasks[bpy.context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id
@@ -789,7 +795,7 @@ class Sequence(bonsai.core.tool.Sequence):
cls.load_task_tree(work_schedule) cls.load_task_tree(work_schedule)
cls.load_task_properties() cls.load_task_properties()
task_props = bpy.context.scene.BIMTaskTreeProperties task_props = cls.get_task_tree_props()
expanded_tasks = [item.ifc_definition_id for item in task_props.tasks] expanded_tasks = [item.ifc_definition_id for item in task_props.tasks]
bpy.context.scene.BIMWorkScheduleProperties.active_task_index = expanded_tasks.index(task.id()) or 0 bpy.context.scene.BIMWorkScheduleProperties.active_task_index = expanded_tasks.index(task.id()) or 0
+10 -6
View File
@@ -1133,23 +1133,27 @@ class Spatial(bonsai.core.tool.Spatial):
@classmethod @classmethod
def toggle_spaces_visibility_wired_and_textured(cls, spaces: list[ifcopenshell.entity_instance]) -> None: def toggle_spaces_visibility_wired_and_textured(cls, spaces: list[ifcopenshell.entity_instance]) -> None:
first_obj = tool.Ifc.get_object(spaces[0]) first_obj = tool.Ifc.get_object(spaces[0])
if bpy.data.objects[first_obj.name].display_type == "TEXTURED": assert isinstance(first_obj, bpy.types.Object)
obj: bpy.types.Object
if first_obj.display_type == "TEXTURED":
for space in spaces: for space in spaces:
obj = tool.Ifc.get_object(space) obj = tool.Ifc.get_object(space)
bpy.data.objects[obj.name].show_wire = True obj.show_wire = True
bpy.data.objects[obj.name].display_type = "WIRE" obj.display_type = "WIRE"
return return
elif bpy.data.objects[first_obj.name].display_type == "WIRE": elif first_obj.display_type == "WIRE":
for space in spaces: for space in spaces:
obj = tool.Ifc.get_object(space) obj = tool.Ifc.get_object(space)
bpy.data.objects[obj.name].show_wire = False obj.show_wire = False
bpy.data.objects[obj.name].display_type = "TEXTURED" obj.display_type = "TEXTURED"
return return
@classmethod @classmethod
def toggle_hide_spaces(cls, spaces: list[ifcopenshell.entity_instance]) -> None: def toggle_hide_spaces(cls, spaces: list[ifcopenshell.entity_instance]) -> None:
first_obj = tool.Ifc.get_object(spaces[0]) first_obj = tool.Ifc.get_object(spaces[0])
assert isinstance(first_obj, bpy.types.Object)
obj: bpy.types.Object
if first_obj.hide_get() == False: if first_obj.hide_get() == False:
for space in spaces: for space in spaces:
obj = tool.Ifc.get_object(space) obj = tool.Ifc.get_object(space)
+2 -1
View File
@@ -352,7 +352,8 @@ def i_select_the_item_name_item_in_the_list_name_list(item_name, list_name):
@when("I load a new pset template file") @when("I load a new pset template file")
def i_load_a_new_pset_template_file(): def i_load_a_new_pset_template_file():
IfcStore.pset_template_path = bpy.context.scene.BIMPsetTemplateProperties.pset_template_files props = tool.PsetTemplate.get_pset_template_props()
IfcStore.pset_template_path = props.pset_template_files
IfcStore.pset_template_file = ifcopenshell.open(IfcStore.pset_template_path) IfcStore.pset_template_file = ifcopenshell.open(IfcStore.pset_template_path)
@@ -21,6 +21,7 @@ import ifcopenshell.api.owner
import ifcopenshell.api.geometry import ifcopenshell.api.geometry
import ifcopenshell.guid import ifcopenshell.guid
import ifcopenshell.util.placement import ifcopenshell.util.placement
from typing import Any
def assign_port( def assign_port(
@@ -68,6 +69,9 @@ def assign_port(
class Usecase: class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self): def execute(self):
if self.file.schema == "IFC2X3": if self.file.schema == "IFC2X3":
return self.execute_ifc2x3() return self.execute_ifc2x3()