mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
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:
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user