update qto core and qto tools:

- when calculating quantities of an object, assign base qto to object if none exists.
- optimise core and tool functions so it's faster for many selected objects
This commit is contained in:
Sigma Dimensions
2023-01-19 21:05:47 +01:00
parent 07ee62eb91
commit 7a3e53a9f8
8 changed files with 105 additions and 121 deletions
@@ -28,7 +28,6 @@ classes = (
operator.EnablePsetEditing, operator.EnablePsetEditing,
operator.GuessQuantity, operator.GuessQuantity,
operator.CalculateQuantity, operator.CalculateQuantity,
operator.CalculateAllQuantities,
operator.RemovePset, operator.RemovePset,
operator.TogglePsetExpansion, operator.TogglePsetExpansion,
operator.BIM_OT_add_property_to_edit, operator.BIM_OT_add_property_to_edit,
@@ -438,20 +438,6 @@ class GuessQuantity(bpy.types.Operator):
return unit_settings.length_unit[0 : -len("METERS")], "METRE" return unit_settings.length_unit[0 : -len("METERS")], "METRE"
class CalculateAllQuantities(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.calculate_all_quantities"
bl_label = "Calculate All Quantities"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Calculate all quantities for all selected objects"
@classmethod
def poll(cls, context):
return context.active_object
def _execute(self, context):
QtoCore.calculate_all_qtos(tool.Qto, selected_objects = [context.active_object])
class CopyPropertyToSelection(bpy.types.Operator, Operator): class CopyPropertyToSelection(bpy.types.Operator, Operator):
bl_idname = "bim.copy_property_to_selection" bl_idname = "bim.copy_property_to_selection"
bl_label = "Copy Property To Selection" bl_label = "Copy Property To Selection"
@@ -26,8 +26,8 @@ classes = (
operator.CalculateObjectVolumes, operator.CalculateObjectVolumes,
operator.ExecuteQtoMethod, operator.ExecuteQtoMethod,
operator.QuantifyObjects, operator.QuantifyObjects,
operator.AssignPsetQto, operator.AssignBaseQto,
operator.CalculateAllQtos, operator.CalculateAllQuantities,
prop.BIMQtoProperties, prop.BIMQtoProperties,
ui.BIM_PT_qto_utilities, ui.BIM_PT_qto_utilities,
) )
@@ -172,30 +172,30 @@ class QuantifyObjects(bpy.types.Operator):
PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id) PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"} return {"FINISHED"}
class AssignPsetQto(bpy.types.Operator, tool.Ifc.Operator): class AssignBaseQto(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_pset_qto" bl_idname = "bim.assign_objects_base_qto"
bl_label = "Assign IFC Object Quantity Set" bl_label = "Assign IFC Object Quantity Set"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Assign IFC quantity set to selected object" bl_description = "Assign IFC quantity set to selected object"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.active_object and context.selected_objects return tool.Ifc.get() and context.selected_objects
def _execute(self, context): def _execute(self, context):
core.assign_pset_qto(tool.Qto, selected_objects = context.selected_objects) core.assign_objects_base_qto(tool.Ifc, tool.Qto, selected_objects = context.selected_objects)
return {"FINISHED"} return {"FINISHED"}
class CalculateAllQtos(bpy.types.Operator, tool.Ifc.Operator): class CalculateAllQuantities(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.calculate_all_qtos" bl_idname = "bim.calculate_all_quantities"
bl_label = "Calculate all quantities" bl_label = "Calculate all quantities"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Calculate all possible quantities and assign them to selected object" bl_description = "Calculate all possible quantities and assign them to selected object"
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.active_object and context.selected_objects return tool.Ifc.get() and context.selected_objects
def _execute(self, context): def _execute(self, context):
core.calculate_all_qtos(tool.Qto, selected_objects = context.selected_objects) core.calculate_objects_base_quantities(tool.Ifc, tool.Qto, QtoCalculator(), selected_objects = context.selected_objects)
return {"FINISHED"} return {"FINISHED"}
@@ -53,7 +53,7 @@ class BIM_PT_qto_utilities(Panel):
row.operator("bim.quantify_objects", icon="COPYDOWN", text="") row.operator("bim.quantify_objects", icon="COPYDOWN", text="")
row = layout.row(align=True) row = layout.row(align=True)
row.operator("bim.assign_pset_qto") row.operator("bim.assign_objects_base_qto")
row = layout.row(align=True) row = layout.row(align=True)
row.operator("bim.calculate_all_qtos") row.operator("bim.calculate_all_quantities", icon="MOD_EDGESPLIT")
+36 -20
View File
@@ -22,30 +22,46 @@ def calculate_circle_radius(qto, obj=None):
qto.set_qto_result(result) qto.set_qto_result(result)
return result return result
def assign_pset_qto(qto, selected_objects):
def assign_objects_base_qto(ifc, qto, selected_objects):
for obj in selected_objects: for obj in selected_objects:
qto.assign_base_qto_to_object(obj) assign_object_base_qto(ifc, qto, obj)
def calculate_all_qtos(qto, selected_objects):
for obj in selected_objects:
if not qto.get_qto_entity(obj):
print(f"There is no pset qto instance associated to object {obj.name}")
continue
applicable_quantity_names = qto.get_applicable_quantity_names(obj) def assign_object_base_qto(ifc, qto, obj):
product = ifc.get_entity(obj)
if not product:
return
base_quantity_name = qto.get_applicable_base_quantity_name(product)
if not base_quantity_name:
return
ifc.run(
"pset.add_qto",
product=product,
name=base_quantity_name,
)
calculated_quantities = qto.get_calculated_quantities(obj, applicable_quantity_names)
qto.edit_qto(obj, calculated_quantities) def calculate_objects_base_quantities(ifc, qto, calculator, selected_objects):
if selected_objects:
for obj in selected_objects:
calculate_object_base_quantities(ifc, qto, calculator, obj)
def guess_all_qtos(qto, selected_objects):
for obj in selected_objects:
if not qto.get_qto_entity(obj):
print(f"There is no pset qto instance associated to object {obj.name}")
continue
applicable_quantity_names = qto.get_applicable_quantity_names(obj) def calculate_object_base_quantities(ifc, qto, calculator, obj):
product = ifc.get_entity(obj)
calculated_quantities = qto.get_calculated_quantities(obj, applicable_quantity_names) if not product:
return
qto.edit_qto(obj, calculated_quantities) base_quantity_name = qto.get_applicable_base_quantity_name(product)
if not base_quantity_name:
print(f"There is no base quantity")
return
base_qto = qto.get_base_qto(product)
if not base_qto:
base_qto = ifc.run(
"pset.add_qto",
product=product,
name=base_quantity_name,
)
calculated_quantities = qto.calculate_object_quantities(calculator, base_quantity_name, obj)
ifc.run("pset.edit_qto", qto=base_qto, properties=calculated_quantities)
+9 -7
View File
@@ -404,14 +404,16 @@ class Pset:
class Qto: class Qto:
def get_radius_of_selected_vertices(cls, obj): pass def get_radius_of_selected_vertices(cls, obj): pass
def set_qto_result(cls, result): pass def set_qto_result(cls, result): pass
def assign_base_qto_to_object(cls, object): pass def get_applicable_quantity_names(cls, qto_name): pass
def get_applicable_quantity_names(cls, object): pass def get_applicable_base_quantity_name(cls, object): pass
def get_qto_applicable_name(cls, object): pass
def edit_qto(cls, object, calculated_quantities): pass
def get_qto_entity(cls, object): pass
def get_new_quantity(cls, object, quantity_name, alternative_prop_names): pass
def get_rounded_value(cls, new_quantity): pass def get_rounded_value(cls, new_quantity): pass
def get_calculated_quantities(cls, object, pset_qto_properties): pass def calculate_object_quantities(cls, calculator, baste_qto, object): pass
def add_object_base_qto(cls, object): pass
def add_product_base_qto(cls, product): pass
def get_applicable_base_quantity_names(cls, product): pass
def get_new_calculated_quantity(cls, qto_name, quantity_name, object): pass
def get_new_guessed_quantity(cls, object, qto_name, quantity_name, ): pass
@interface @interface
class Resource: class Resource:
+47 -66
View File
@@ -25,6 +25,7 @@ from mathutils import Vector
from ifcopenshell import util from ifcopenshell import util
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
import blenderbim.bim.schema
class Qto(blenderbim.core.tool.Qto): class Qto(blenderbim.core.tool.Qto):
@@ -42,87 +43,55 @@ class Qto(blenderbim.core.tool.Qto):
bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3)) bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
@classmethod @classmethod
def assign_base_qto_to_object(cls, obj): def add_object_base_qto(cls, obj):
file = tool.Ifc.get() product = tool.Ifc.get_entity(obj)
entity = tool.Ifc.get_entity(obj) cls.add_product_base_qto(product)
pset_qto_name = cls.get_qto_applicable_name(obj)
ifcopenshell.api.run(
"pset.add_qto",
file,
**{
"product": entity,
"name": pset_qto_name,
},
)
@classmethod @classmethod
def get_applicable_quantity_names(cls, obj): def add_product_base_qto(cls, product):
file = tool.Ifc.get() base_quantity_name = cls.get_applicable_base_quantity_name(product)
schema = file.schema if base_quantity_name:
pset_qto = util.pset.PsetQto(schema) return tool.Ifc.run(
qto_name = cls.get_qto_applicable_name(obj) "pset.add_qto",
properties_templates = pset_qto.get_by_name(qto_name).get_info()['HasPropertyTemplates'] product=product,
applicable_quantity_names = [a.get_info()['Name'] for a in properties_templates] name=base_quantity_name,
return applicable_quantity_names
@classmethod
def get_qto_applicable_name(cls, obj):
entity = tool.Ifc.get_entity(obj)
ifc_class = entity.get_info()['type']
qto_applicable_names = blenderbim.bim.schema.ifc.psetqto.get_applicable_names(ifc_class, qto_only=True)
qto_applicable_name = [q for q in qto_applicable_names if "Qto_" in q and "Base" in q][0]
return qto_applicable_name
@classmethod
def edit_qto(cls, obj, calculated_quantities):
file = tool.Ifc.get()
pset_qto_applicable_name = cls.get_qto_applicable_name(obj)
qto_entity = cls.get_qto_entity(obj)
ifcopenshell.api.run("pset.edit_qto",
file,
qto = qto_entity, properties = calculated_quantities,
) )
@classmethod @classmethod
def get_qto_entity(cls, obj): def get_applicable_quantity_names(cls, qto_name):
file = tool.Ifc.get() pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(qto_name)
entity = tool.Ifc.get_entity(obj) return (
qto_applicable_name = cls.get_qto_applicable_name(obj) [property.Name for property in pset_template.HasPropertyTemplates]
qto_entity = tool.Pset.get_element_pset(entity, qto_applicable_name) if hasattr(pset_template, "HasPropertyTemplates")
return qto_entity else []
)
@classmethod
def get_applicable_base_quantity_name(cls, product=None):
if not product:
return
applicable_qto_names = blenderbim.bim.schema.ifc.psetqto.get_applicable_names(product.is_a(), qto_only=True)
return next((qto_name for qto_name in applicable_qto_names if "Qto_" in qto_name and "Base" in qto_name), None)
@classmethod @classmethod
def get_new_calculated_quantity(cls, qto_name, quantity_name, obj): def get_new_calculated_quantity(cls, qto_name, quantity_name, obj):
calculator = QtoCalculator() return QtoCalculator().calculate_quantity(qto_name, quantity_name, obj)
new_quantity = calculator.calculate_quantity(qto_name, quantity_name, obj)
return new_quantity
@classmethod @classmethod
def get_new_guessed_quantity(cls, obj, quantity_name, alternative_prop_names): def get_new_guessed_quantity(cls, obj, quantity_name, alternative_prop_names):
calculator = QtoCalculator() return QtoCalculator().guess_quantity(quantity_name, alternative_prop_names, obj)
new_quantity = calculator.guess_quantity(quantity_name, alternative_prop_names, obj)
@classmethod @classmethod
def get_rounded_value(cls, new_quantity): def get_rounded_value(cls, new_quantity):
return round(new_quantity, 3) return round(new_quantity, 3)
@classmethod @classmethod
def get_calculated_quantities(cls, obj, applicable_quantity_names): def calculate_object_quantities(cls, calculator, qto_name, obj):
calculated_quantities = {} return {
qto_name = cls.get_qto_applicable_name(obj) quantity_name: cls.get_rounded_value(calculator.calculate_quantity(qto_name, quantity_name, obj))
calculator = QtoCalculator() for quantity_name in cls.get_applicable_quantity_names(qto_name) or []
if cls.has_calculator(qto_name, quantity_name)
for quantity_name in applicable_quantity_names: }
if not cls.has_calculator(qto_name, quantity_name):
continue
else:
new_quantity = calculator.calculate_quantity(qto_name, quantity_name, obj)
new_quantity = cls.get_rounded_value(new_quantity)
calculated_quantities[quantity_name] = new_quantity
return calculated_quantities
@classmethod @classmethod
def has_calculator(cls, qto_name, quantity_name): def has_calculator(cls, qto_name, quantity_name):
@@ -132,8 +101,8 @@ class Qto(blenderbim.core.tool.Qto):
def get_guessed_quantities(cls, obj, pset_qto_properties): def get_guessed_quantities(cls, obj, pset_qto_properties):
calculated_quantities = {} calculated_quantities = {}
for pset_qto_property in pset_qto_properties: for pset_qto_property in pset_qto_properties:
quantity_name = pset_qto_property.get_info()['Name'] quantity_name = pset_qto_property.get_info()["Name"]
alternative_prop_names = [p.get_info()['Name'] for p in pset_qto_properties] alternative_prop_names = [p.get_info()["Name"] for p in pset_qto_properties]
new_quantity = cls.get_new_guessed_quantity(obj, quantity_name, alternative_prop_names) new_quantity = cls.get_new_guessed_quantity(obj, quantity_name, alternative_prop_names)
@@ -146,3 +115,15 @@ class Qto(blenderbim.core.tool.Qto):
return calculated_quantities return calculated_quantities
@classmethod
def get_base_qto(cls, product):
if not hasattr(product, "IsDefinedBy"):
return
for rel in product.IsDefinedBy or []:
if not (
rel.is_a("IfcRelDefinesByProperties")
and "Base" in rel.RelatingPropertyDefinition.Name
and "Qto_" in rel.RelatingPropertyDefinition.Name
):
continue
return rel.RelatingPropertyDefinition