From b673fc94a518523da0efad6943a03ef0fea7fa2b Mon Sep 17 00:00:00 2001 From: Massimo Fabbro <79401028+maxfb87@users.noreply.github.com> Date: Mon, 26 Sep 2022 08:05:08 +0200 Subject: [PATCH] Add calculate all qtos button (#2441) * Add button to calculate all object qtos in qto n-panel * Calculate all qtos button: now the schema is readed from the file * Calculate all qtos button: now edit_qto runs only one time every object * Add button to assign pset qto to selected objects * Calculate all qtos button: small fixing and unable to execute if no selected objects * Calculate all qtos button: first tool test * Calculate all qtos button: use better built-in function to get ifc obj entity --- .../blenderbim/bim/module/qto/__init__.py | 2 + .../blenderbim/bim/module/qto/operator.py | 28 +++++ .../blenderbim/bim/module/qto/ui.py | 6 + src/blenderbim/blenderbim/core/qto.py | 19 +++ src/blenderbim/blenderbim/core/tool.py | 14 ++- src/blenderbim/blenderbim/tool/qto.py | 115 ++++++++++++++++++ src/blenderbim/test/tool/test_qto.py | 19 +++ 7 files changed, 202 insertions(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/qto/__init__.py b/src/blenderbim/blenderbim/bim/module/qto/__init__.py index aab4c49a2a..98ff7c2589 100644 --- a/src/blenderbim/blenderbim/bim/module/qto/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/qto/__init__.py @@ -26,6 +26,8 @@ classes = ( operator.CalculateObjectVolumes, operator.ExecuteQtoMethod, operator.QuantifyObjects, + operator.AssignPsetQto, + operator.CalculateAllQtos, prop.BIMQtoProperties, ui.BIM_PT_qto_utilities, ) diff --git a/src/blenderbim/blenderbim/bim/module/qto/operator.py b/src/blenderbim/blenderbim/bim/module/qto/operator.py index 90c55ac401..232dd399e8 100644 --- a/src/blenderbim/blenderbim/bim/module/qto/operator.py +++ b/src/blenderbim/blenderbim/bim/module/qto/operator.py @@ -150,3 +150,31 @@ class QuantifyObjects(bpy.types.Operator): ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={props.prop_name: result}) PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id) return {"FINISHED"} + +class AssignPsetQto(bpy.types.Operator): + bl_idname = "bim.assign_pset_qto" + bl_label = "Assign Pset Qto" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Assign pset qto to selected object" + + @classmethod + def poll(cls, context): + return context.active_object and context.selected_objects + + def execute(self, context): + core.assign_pset_qto(tool.Qto, selected_objects = context.selected_objects) + return {"FINISHED"} + +class CalculateAllQtos(bpy.types.Operator): + bl_idname = "bim.calculate_all_qtos" + bl_label = "Calculate All Qtos" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Calculate all pset qtos properties of objects assigned pset qto" + + @classmethod + def poll(cls, context): + return context.active_object and context.selected_objects + + def execute(self, context): + core.calculate_all_qtos(tool.Qto, selected_objects = context.selected_objects) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/qto/ui.py b/src/blenderbim/blenderbim/bim/module/qto/ui.py index 58699f609b..f3629ae5b7 100644 --- a/src/blenderbim/blenderbim/bim/module/qto/ui.py +++ b/src/blenderbim/blenderbim/bim/module/qto/ui.py @@ -51,3 +51,9 @@ class BIM_PT_qto_utilities(Panel): row.prop(props, "qto_name", text="") row.prop(props, "prop_name", text="") row.operator("bim.quantify_objects", icon="COPYDOWN", text="") + + row = layout.row(align=True) + row.operator("bim.assign_pset_qto") + + row = layout.row(align=True) + row.operator("bim.calculate_all_qtos") diff --git a/src/blenderbim/blenderbim/core/qto.py b/src/blenderbim/blenderbim/core/qto.py index 28c061f9da..92284b925e 100644 --- a/src/blenderbim/blenderbim/core/qto.py +++ b/src/blenderbim/blenderbim/core/qto.py @@ -21,3 +21,22 @@ def calculate_circle_radius(qto, obj=None): result = qto.get_radius_of_selected_vertices(obj) qto.set_qto_result(result) return result + +def assign_pset_qto(qto, selected_objects): + for object in selected_objects: + qto.set_active_object(object) + qto.assign_pset_qto_to_selected_object(object) + +def calculate_all_qtos(qto, selected_objects): + for object in selected_objects: + qto.set_active_object(object) + + if not qto.get_pset_qto_object_ifc_info(object): + print("There is no pset qto instance associated to object " + object.name) + continue + + pset_qto_properties = qto.get_pset_qto_properties(object) + + calculated_quantities = qto.get_calculated_quantities(object, pset_qto_properties) + + qto.edit_qto(object, calculated_quantities) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index eb844155d9..23c7d511c7 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -401,7 +401,19 @@ class Pset: class Qto: def get_radius_of_selected_vertices(cls, obj): pass def set_qto_result(cls, result): pass - + def set_active_object(cls, object): pass + def get_pset_qto_object_ifc_info(cls, object): pass + def get_pset_qto_properties(cls, object): pass + def get_pset_qto_name(cls, object): pass + def get_applicable_pset_names(cls, object): pass + def edit_qto(cls, object, calculated_quantities): pass + def get_pset_qto_id(cls, object): pass + def get_pset_qto_name(cls, object): pass + def get_new_quantity(cls, object, quantity_name, alternative_prop_names): pass + def get_non_calculated_value(cls): pass + def get_rounded_value(cls, new_quantity): pass + def get_calculated_quantities(cls, object, pset_qto_properties): pass + def assign_pset_qto_to_selected_object(cls, object): pass @interface class Resource: diff --git a/src/blenderbim/blenderbim/tool/qto.py b/src/blenderbim/blenderbim/tool/qto.py index 732193f818..855a47ec05 100644 --- a/src/blenderbim/blenderbim/tool/qto.py +++ b/src/blenderbim/blenderbim/tool/qto.py @@ -16,10 +16,15 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from types import ClassMethodDescriptorType import bpy import blenderbim.core.tool import blenderbim.tool as tool +import ifcopenshell from mathutils import Vector +from ifcopenshell import util +from blenderbim.bim.module.pset.qto_calculator import QtoCalculator + class Qto(blenderbim.core.tool.Qto): @@ -35,3 +40,113 @@ class Qto(blenderbim.core.tool.Qto): @classmethod def set_qto_result(cls, result): bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3)) + + @classmethod + def set_active_object(cls, object): + bpy.context.view_layer.objects.active = object + + @classmethod + def get_pset_qto_object_ifc_info(cls, object): + element = tool.Ifc.get_entity(object) + pset_qto_ifc_instance = ifcopenshell.util.element.get_psets(element, qtos_only = True) + return pset_qto_ifc_instance + + @classmethod + def get_pset_qto_properties(cls, object): + file = tool.Ifc.get() + schema = file.schema + pset_qto = util.pset.PsetQto(schema) + pset_qto_name = cls.get_pset_qto_name(object) + pset_qto_properties = pset_qto.get_by_name(pset_qto_name).get_info()['HasPropertyTemplates'] + return pset_qto_properties + + @classmethod + def get_pset_qto_name(cls, object): + applicable_pset_names = cls.get_applicable_pset_names(object) + for applicable_pset_name in applicable_pset_names: + if 'Qto_' in applicable_pset_name: + pset_qto_name = applicable_pset_name + return pset_qto_name + + @classmethod + def get_applicable_pset_names(cls, object): + file = tool.Ifc.get() + schema = file.schema + pset_qto = util.pset.PsetQto(schema) + entity = tool.Ifc.get_entity(object) + ifc_object_type = entity.get_info()['type'] + applicable_pset_names = pset_qto.get_applicable_names(ifc_object_type) + return applicable_pset_names + + @classmethod + def edit_qto(cls, object, calculated_quantities): + file = tool.Ifc.get() + pset_qto_id = cls.get_pset_qto_id(object) + pset_qto_name = cls.get_pset_qto_name(object) + + ifcopenshell.api.run("pset.edit_qto", + file, + **{"qto" : pset_qto_id, "name" : pset_qto_name, "properties": calculated_quantities} + ) + + @classmethod + def get_pset_qto_id(cls, object): + file = tool.Ifc.get() + pset_qto_name = cls.get_pset_qto_name(object) + pset_qto_object_ifc_instance = cls.get_pset_qto_object_ifc_info(object) + pset_qto_id = file.by_id(pset_qto_object_ifc_instance[pset_qto_name]['id']) + return pset_qto_id + + @classmethod + def get_pset_qto_name(cls, object): + applicable_pset_names = cls.get_applicable_pset_names(object) + for applicable_pset_name in applicable_pset_names: + if 'Qto_' in applicable_pset_name: + pset_qto_name = applicable_pset_name + return pset_qto_name + + @classmethod + def get_new_quantity(cls, object, quantity_name, alternative_prop_names): + calculator = QtoCalculator() + new_quantity = calculator.guess_quantity(quantity_name, alternative_prop_names, object) + return new_quantity + + @classmethod + def get_non_calculated_value(cls): + return 0 + + @classmethod + def get_rounded_value(cls, new_quantity): + return round(new_quantity, 3) + + @classmethod + def get_calculated_quantities(cls, object, pset_qto_properties): + calculated_quantities = {} + for pset_qto_property in pset_qto_properties: + quantity_name = pset_qto_property.get_info()['Name'] + alternative_prop_names = [p.get_info()['Name'] for p in pset_qto_properties] + + new_quantity = cls.get_new_quantity(object, quantity_name, alternative_prop_names) + + if not new_quantity: + new_quantity = cls.get_non_calculated_value() + else: + new_quantity = cls.get_rounded_value(new_quantity) + + calculated_quantities[quantity_name] = new_quantity + + return calculated_quantities + + @classmethod + def assign_pset_qto_to_selected_object(cls, object): + file = tool.Ifc.get() + entity = tool.Ifc.get_entity(object) + pset_qto_name = cls.get_pset_qto_name(object) + ifcopenshell.api.run( + "pset.add_qto", + file, + **{ + "product": entity, + "name": pset_qto_name, + }, + ) diff --git a/src/blenderbim/test/tool/test_qto.py b/src/blenderbim/test/tool/test_qto.py index bc16ad5e9c..3803b5f038 100644 --- a/src/blenderbim/test/tool/test_qto.py +++ b/src/blenderbim/test/tool/test_qto.py @@ -39,3 +39,22 @@ class TestSetQtoResult(test.bim.bootstrap.NewFile): def test_run(self): subject.set_qto_result(123.4567) assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457" + +class TestGetPsetQtoObjectIfcInfo(test.bim.bootstrap.NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + wall = ifc.createIfcWall() + wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh")) + tool.Ifc.link(wall, wall_obj) + pset_qto = ifcopenshell.api.run("pset.add_qto", ifc, product=wall, name="Qto_foo") + assert subject.get_pset_qto_object_ifc_info(wall_obj)['Qto_foo']['id'] == pset_qto.get_info()['id'] + assert list(subject.get_pset_qto_object_ifc_info(wall_obj).keys())[0] == pset_qto.get_info()['Name'] + + def test_isempty(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + wall = ifc.createIfcWall() + wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh")) + tool.Ifc.link(wall, wall_obj) + assert not subject.get_pset_qto_object_ifc_info(wall_obj) == True