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.GuessQuantity,
operator.CalculateQuantity,
operator.CalculateAllQuantities,
operator.RemovePset,
operator.TogglePsetExpansion,
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"
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):
bl_idname = "bim.copy_property_to_selection"
bl_label = "Copy Property To Selection"
@@ -26,8 +26,8 @@ classes = (
operator.CalculateObjectVolumes,
operator.ExecuteQtoMethod,
operator.QuantifyObjects,
operator.AssignPsetQto,
operator.CalculateAllQtos,
operator.AssignBaseQto,
operator.CalculateAllQuantities,
prop.BIMQtoProperties,
ui.BIM_PT_qto_utilities,
)
@@ -172,30 +172,30 @@ class QuantifyObjects(bpy.types.Operator):
PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class AssignPsetQto(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_pset_qto"
class AssignBaseQto(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_objects_base_qto"
bl_label = "Assign IFC Object Quantity Set"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Assign IFC quantity set to selected object"
@classmethod
def poll(cls, context):
return context.active_object and context.selected_objects
return tool.Ifc.get() and context.selected_objects
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"}
class CalculateAllQtos(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.calculate_all_qtos"
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 possible quantities and assign them to selected object"
@classmethod
def poll(cls, context):
return context.active_object and context.selected_objects
return tool.Ifc.get() and context.selected_objects
def _execute(self, context):
core.calculate_all_qtos(tool.Qto, selected_objects = context.selected_objects)
return {"FINISHED"}
core.calculate_objects_base_quantities(tool.Ifc, tool.Qto, QtoCalculator(), selected_objects = context.selected_objects)
return {"FINISHED"}
@@ -53,7 +53,7 @@ class BIM_PT_qto_utilities(Panel):
row.operator("bim.quantify_objects", icon="COPYDOWN", text="")
row = layout.row(align=True)
row.operator("bim.assign_pset_qto")
row.operator("bim.assign_objects_base_qto")
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)
return result
def assign_pset_qto(qto, selected_objects):
def assign_objects_base_qto(ifc, qto, 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)
calculated_quantities = qto.get_calculated_quantities(obj, applicable_quantity_names)
qto.edit_qto(obj, calculated_quantities)
def calculate_object_base_quantities(ifc, qto, calculator, 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:
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:
def get_radius_of_selected_vertices(cls, obj): pass
def set_qto_result(cls, result): pass
def assign_base_qto_to_object(cls, object): pass
def get_applicable_quantity_names(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_applicable_quantity_names(cls, qto_name): pass
def get_applicable_base_quantity_name(cls, object): 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
class Resource:
+47 -66
View File
@@ -25,6 +25,7 @@ from mathutils import Vector
from ifcopenshell import util
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
import blenderbim.bim.schema
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))
@classmethod
def assign_base_qto_to_object(cls, obj):
file = tool.Ifc.get()
entity = tool.Ifc.get_entity(obj)
pset_qto_name = cls.get_qto_applicable_name(obj)
ifcopenshell.api.run(
"pset.add_qto",
file,
**{
"product": entity,
"name": pset_qto_name,
},
)
def add_object_base_qto(cls, obj):
product = tool.Ifc.get_entity(obj)
cls.add_product_base_qto(product)
@classmethod
def get_applicable_quantity_names(cls, obj):
file = tool.Ifc.get()
schema = file.schema
pset_qto = util.pset.PsetQto(schema)
qto_name = cls.get_qto_applicable_name(obj)
properties_templates = pset_qto.get_by_name(qto_name).get_info()['HasPropertyTemplates']
applicable_quantity_names = [a.get_info()['Name'] for a in properties_templates]
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,
def add_product_base_qto(cls, product):
base_quantity_name = cls.get_applicable_base_quantity_name(product)
if base_quantity_name:
return tool.Ifc.run(
"pset.add_qto",
product=product,
name=base_quantity_name,
)
@classmethod
def get_qto_entity(cls, obj):
file = tool.Ifc.get()
entity = tool.Ifc.get_entity(obj)
qto_applicable_name = cls.get_qto_applicable_name(obj)
qto_entity = tool.Pset.get_element_pset(entity, qto_applicable_name)
return qto_entity
def get_applicable_quantity_names(cls, qto_name):
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(qto_name)
return (
[property.Name for property in pset_template.HasPropertyTemplates]
if hasattr(pset_template, "HasPropertyTemplates")
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
def get_new_calculated_quantity(cls, qto_name, quantity_name, obj):
calculator = QtoCalculator()
new_quantity = calculator.calculate_quantity(qto_name, quantity_name, obj)
return new_quantity
return QtoCalculator().calculate_quantity(qto_name, quantity_name, obj)
@classmethod
def get_new_guessed_quantity(cls, obj, quantity_name, alternative_prop_names):
calculator = QtoCalculator()
new_quantity = calculator.guess_quantity(quantity_name, alternative_prop_names, obj)
return QtoCalculator().guess_quantity(quantity_name, alternative_prop_names, obj)
@classmethod
def get_rounded_value(cls, new_quantity):
return round(new_quantity, 3)
@classmethod
def get_calculated_quantities(cls, obj, applicable_quantity_names):
calculated_quantities = {}
qto_name = cls.get_qto_applicable_name(obj)
calculator = QtoCalculator()
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
def calculate_object_quantities(cls, calculator, qto_name, obj):
return {
quantity_name: cls.get_rounded_value(calculator.calculate_quantity(qto_name, quantity_name, obj))
for quantity_name in cls.get_applicable_quantity_names(qto_name) or []
if cls.has_calculator(qto_name, quantity_name)
}
@classmethod
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):
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]
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_guessed_quantity(obj, quantity_name, alternative_prop_names)
@@ -146,3 +115,15 @@ class Qto(blenderbim.core.tool.Qto):
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