diff --git a/src/blenderbim/blenderbim/bim/module/pset/calc_quantity_function_mapper.py b/src/blenderbim/blenderbim/bim/module/pset/calc_quantity_function_mapper.py index c27349d1f3..17a8b964ae 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/calc_quantity_function_mapper.py +++ b/src/blenderbim/blenderbim/bim/module/pset/calc_quantity_function_mapper.py @@ -92,16 +92,16 @@ mapper = { 'Volume' : "get_net_volume", }, 'Qto_SignBaseQuantities' : { - 'Height' : None, - 'Width' : None, - 'Thickness' : None, + 'Height' : "get_height", + 'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"}, + 'Thickness' : "get_width", 'Weight' : None, }, 'Qto_CableSegmentBaseQuantities' : { 'GrossWeight' : None, - 'Length' : None, + 'Length' : "get_length", 'CrossSectionArea' : None, - 'OuterSurfaceArea' : None, + 'OuterSurfaceArea' : "get_outer_surface_area", }, 'Qto_BuildingBaseQuantities' : { 'Height' : None, @@ -119,16 +119,16 @@ mapper = { 'GrossWeight' : None, }, 'Qto_RailBaseQuantities' : { - 'Length' : None, - 'Volume' : None, + 'Length' : "get_length", + 'Volume' : "get_net_volume", 'Weight' : None, }, 'Qto_PictorialSignQuantities' : { - 'Area' : None, + 'Area' : "get_net_side_area", 'SignArea' : None, }, 'Qto_SpaceHeaterBaseQuantities' : { - 'Length' : None, + 'Length' : "get_length", 'GrossWeight' : None, 'NetWeight' : None, }, @@ -158,29 +158,29 @@ mapper = { 'Qto_JunctionBoxBaseQuantities' : { 'GrossWeight' : None, 'NumberOfGangs' : None, - 'Length' : None, - 'Width' : None, - 'Height' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Height' : "get_height", }, 'Qto_ArealStratumBaseQuantities' : { - 'Area' : None, - 'Length' : None, + 'Area' : "get_net_footprint_area", + 'Length' : "get_length", 'PlanLength' : None, }, 'Qto_SiteBaseQuantities' : { - 'GrossPerimeter' : None, - 'GrossArea' : None, + 'GrossPerimeter' : "get_gross_perimeter", + 'GrossArea' : "get_gross_footprint_area", }, 'Qto_MotorConnectionBaseQuantities' : { 'GrossWeight' : None, }, 'Qto_RoofBaseQuantities' : { - 'GrossArea' : None, - 'NetArea' : None, + 'GrossArea' : "get_gross_footprint_area", + 'NetArea' : "get_net_footprint_area", 'ProjectedArea' : None, }, 'Qto_ChimneyBaseQuantities' : { - 'Length' : None, + 'Length' : "get_height", }, 'Qto_ElectricTimeControlBaseQuantities' : { 'GrossWeight' : None, @@ -318,10 +318,10 @@ mapper = { 'NetWeight' : None, }, 'Qto_EarthworksCutBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Depth' : None, - 'UndisturbedVolume' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Depth' : "get_height", + 'UndisturbedVolume' : "get_net_volume", 'LooseVolume' : None, 'Weight' : None, }, @@ -332,21 +332,21 @@ mapper = { 'GrossWeight' : None, }, 'Qto_KerbBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Height' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Height' : "get_height", 'Depth' : None, - 'Volume' : None, + 'Volume' : "get_net_volume", 'Weight' : None, }, 'Qto_PavementBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Depth' : None, - 'GrossArea' : None, - 'NetArea' : None, - 'GrossVolume' : None, - 'NetVolume' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Depth' : "get_height", + 'GrossArea' : "get_gross_footprint_area", + 'NetArea' : "get_net_footprint_area", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", }, 'Qto_ControllerBaseQuantities' : { 'GrossWeight' : None, @@ -356,12 +356,12 @@ mapper = { 'GrossArea' : None, }, 'Qto_RampFlightBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'GrossArea' : None, - 'NetArea' : None, - 'GrossVolume' : None, - 'NetVolume' : None, + 'Length' : "get_stair_length", + 'Width' : "get_width", + 'GrossArea' : "get_gross_stair_area", + 'NetArea' : "get_net_stair_area", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", }, 'Qto_ElectricApplianceBaseQuantities' : { 'GrossWeight' : None, @@ -373,8 +373,8 @@ mapper = { 'GrossWeight' : None, }, 'Qto_SurfaceFeatureBaseQuantities' : { - 'Area' : None, - 'Length' : None, + 'Area' : "get_net_footprint_area", + 'Length' : "get_length", }, 'Qto_WallBaseQuantities' : { 'Length' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"}, @@ -390,9 +390,9 @@ mapper = { 'NetWeight' : None, }, 'Qto_StairFlightBaseQuantities' : { - 'Length' : None, - 'GrossVolume' : None, - 'NetVolume' : None, + 'Length' : "get_stair_length", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", }, 'Qto_SwitchingDeviceBaseQuantities' : { 'GrossWeight' : None, @@ -401,8 +401,8 @@ mapper = { 'GrossWeight' : None, }, 'Qto_ConstructionMaterialResourceBaseQuantities' : { - 'GrossVolume' : None, - 'NetVolume' : None, + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", 'GrossWeight' : None, 'NetWeight' : None, }, @@ -417,11 +417,11 @@ mapper = { 'GrossWeight' : None, }, 'Qto_DistributionChamberElementBaseQuantities' : { - 'GrossSurfaceArea' : None, - 'NetSurfaceArea' : None, - 'GrossVolume' : None, - 'NetVolume' : None, - 'Depth' : None, + 'GrossSurfaceArea' : "get_gross_surface_area", + 'NetSurfaceArea' : "get_net_surface_area", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", + 'Depth' : "get_length", }, 'Qto_CompressorBaseQuantities' : { 'GrossWeight' : None, @@ -448,11 +448,11 @@ mapper = { 'NetVolume' : "get_space_net_volume", }, 'Qto_CourseBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Thickness' : None, - 'Volume' : None, - 'GrossVolume' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Thickness' : "get_height", + 'Volume' : "get_net_volume", + 'GrossVolume' : "get_gross_volume", 'Weight' : None, }, 'Qto_CondenserBaseQuantities' : { @@ -462,7 +462,7 @@ mapper = { 'GrossWeight' : None, }, 'Qto_RailingBaseQuantities' : { - 'Length' : None, + 'Length' : "get_length", }, 'Qto_TubeBundleBaseQuantities' : { 'GrossWeight' : None, @@ -480,9 +480,9 @@ mapper = { 'NetWeight' : None, }, 'Qto_SleeperBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Height' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Height' : "get_height", }, 'Qto_ProtectiveDeviceBaseQuantities' : { 'GrossWeight' : None, @@ -493,7 +493,7 @@ mapper = { 'Qto_TankBaseQuantities' : { 'GrossWeight' : None, 'NetWeight' : None, - 'TotalSurfaceArea' : None, + 'TotalSurfaceArea' : "get_outer_surface_area", }, 'Qto_AirToAirHeatRecoveryBaseQuantities' : { 'GrossWeight' : None, @@ -514,8 +514,8 @@ mapper = { 'GrossWeight' : None, }, 'Qto_BuildingElementProxyQuantities' : { - 'NetSurfaceArea' : None, - 'NetVolume' : None, + 'NetSurfaceArea' : "get_net_surface_area", + 'NetVolume' : "get_net_volume", }, 'Qto_FanBaseQuantities' : { 'GrossWeight' : None, @@ -530,38 +530,38 @@ mapper = { 'GrossWeight' : None, }, 'Qto_MemberBaseQuantities' : { - 'Length' : None, - 'CrossSectionArea' : None, - 'OuterSurfaceArea' : None, - 'GrossSurfaceArea' : None, - 'NetSurfaceArea' : None, - 'GrossVolume' : None, - 'NetVolume' : None, + 'Length' : "get_length", + 'CrossSectionArea' : "get_cross_section_area", + 'OuterSurfaceArea' : "get_outer_surface_area", + 'GrossSurfaceArea' : "get_gross_surface_area", + 'NetSurfaceArea' : "get_net_surface_area", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", 'GrossWeight' : None, 'NetWeight' : None, }, 'Qto_BodyGeometryValidation' : { - 'GrossSurfaceArea' : None, - 'NetSurfaceArea' : None, - 'GrossVolume' : None, - 'NetVolume' : None, + 'GrossSurfaceArea' : "get_gross_surface_area", + 'NetSurfaceArea' : "get_net_surface_area", + 'GrossVolume' : "get_gross_volume", + 'NetVolume' : "get_net_volume", 'SurfaceGenusBeforeFeatures' : None, 'SurfaceGenusAfterFeatures' : None, }, 'Qto_VolumetricStratumBaseQuantities' : { - 'Area' : None, + 'Area' : "get_net_footprint_area", 'Mass' : None, - 'PlanArea' : None, - 'Volume' : None, + 'PlanArea' : "get_net_footprint_area", + 'Volume' : "get_net_volume", }, 'Qto_SpatialZoneBaseQuantities' : { - 'Length' : None, - 'Width' : None, - 'Height' : None, + 'Length' : "get_length", + 'Width' : "get_width", + 'Height' : "get_height", }, 'Qto_ReinforcingElementBaseQuantities' : { 'Count' : None, - 'Length' : None, + 'Length' : "get_length", 'Weight' : None, }, 'Qto_EvaporativeCoolerBaseQuantities' : { diff --git a/src/blenderbim/blenderbim/bim/module/pset/notes_about_mapped_calculated_quantities.txt b/src/blenderbim/blenderbim/bim/module/pset/notes_about_mapped_calculated_quantities.txt index fd26390d76..db9f8e892c 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/notes_about_mapped_calculated_quantities.txt +++ b/src/blenderbim/blenderbim/bim/module/pset/notes_about_mapped_calculated_quantities.txt @@ -39,5 +39,16 @@ IfcSpace - GrossWallArea IfcSpace - NetWallArea IfcSpace - Calculate only shared volume between IfcColumn and IfcSpace IfcPipeSegment - Gross cross section area +IfcCableSegment - Cross section area Qto_PipeFittingBaseQuantities - Basically all quantities because the object can not be straight +Qto_BuildingBaseQuantities - (n.7) Not sure if it's useful to map this entity because for example BBim doesn't create a building representation +Qto_PictorialSignQuantities - SignArea +Qto_ArealStratumBaseQuantities - PlanLength +Qto_RoofBaseQuantities - ProjectedArea +Qto_EarthworksCutBase - LooseVolume +Qto_KerbBaseQuantities - Depth (not sure what's the difference between depth and width: there are 4 dimensions, i don't understand the difference) +Qto_LinearStratumBaseQuantities - Diameter +Qto_LinearStratumBaseQuantities - Length +Qto_BodyGeometryValidation - SurfaceGenusBeforeFeatures +Qto_BodyGeometryValidation - SurfaceGenusAfterFeatures diff --git a/src/blenderbim/blenderbim/bim/module/pset/qto_calculator.py b/src/blenderbim/blenderbim/bim/module/pset/qto_calculator.py index b4ed236e46..57afa89e17 100644 --- a/src/blenderbim/blenderbim/bim/module/pset/qto_calculator.py +++ b/src/blenderbim/blenderbim/bim/module/pset/qto_calculator.py @@ -120,6 +120,22 @@ class QtoCalculator: length += self.get_edge_distance(o, e) return length + def get_stair_length(self, obj): + length = self.get_length(obj) + height = self.get_height(obj) + stair_length = math.sqrt(pow(length, 2) + pow(height, 2)) + return stair_length + + def get_net_stair_area(self, obj): + OBB_obj = self.get_OBB_object(obj) + OBB_net_footprint_area = self.get_net_footprint_area(OBB_obj) + return OBB_net_footprint_area + + def get_gross_stair_area(self, obj): + OBB_obj = self.get_OBB_object(obj) + OBB_gross_footprint_area = self.get_gross_footprint_area(OBB_obj) + return OBB_gross_footprint_area + def get_width(self, o): """_summary_: Returns the width of the object bounding box @@ -659,7 +675,8 @@ class QtoCalculator: ifc = tool.Ifc.get() ifc_element = ifc.by_id(obj.BIMObjectProperties.ifc_definition_id) - if len(openings := ifc_element.HasOpenings) != 0: +# if len(openings := ifc_element.HasOpenings) != 0: + if len(openings := self.has_openings(obj)) != 0: for opening in openings: if opening.RelatedOpeningElement.PredefinedType == "OPENING": opening_id = opening.RelatedOpeningElement.GlobalId diff --git a/src/blenderbim/blenderbim/bim/module/qto/operator.py b/src/blenderbim/blenderbim/bim/module/qto/operator.py index ed9957768c..0d067c45de 100644 --- a/src/blenderbim/blenderbim/bim/module/qto/operator.py +++ b/src/blenderbim/blenderbim/bim/module/qto/operator.py @@ -174,9 +174,9 @@ class QuantifyObjects(bpy.types.Operator): class AssignPsetQto(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_pset_qto" - bl_label = "Assign Pset Qto" + bl_label = "Assign IFC Object Quantity Set" bl_options = {"REGISTER", "UNDO"} - bl_description = "Assign pset qto to selected object" + bl_description = "Assign IFC quantity set to selected object" @classmethod def poll(cls, context): @@ -188,9 +188,9 @@ class AssignPsetQto(bpy.types.Operator, tool.Ifc.Operator): class CalculateAllQtos(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.calculate_all_qtos" - bl_label = "Calculate All Qtos" + bl_label = "Calculate all quantities" bl_options = {"REGISTER", "UNDO"} - bl_description = "Calculate all pset qtos properties of objects assigned pset qto" + bl_description = "Calculate all possible quantities and assign them to selected object" @classmethod def poll(cls, context): diff --git a/src/blenderbim/blenderbim/core/qto.py b/src/blenderbim/blenderbim/core/qto.py index e8d97058fc..d2e98a667d 100644 --- a/src/blenderbim/blenderbim/core/qto.py +++ b/src/blenderbim/blenderbim/core/qto.py @@ -37,3 +37,15 @@ def calculate_all_qtos(qto, selected_objects): calculated_quantities = qto.get_calculated_quantities(obj, pset_qto_properties) qto.edit_qto(obj, calculated_quantities) + +def guess_all_qtos(qto, selected_objects): + for obj in selected_objects: + if not qto.get_pset_qto_object_ifc_info(obj): + print(f"There is no pset qto instance associated to object {obj.name}") + continue + + pset_qto_properties = qto.get_pset_qto_properties(obj) + + calculated_quantities = qto.get_calculated_quantities(obj, pset_qto_properties) + + qto.edit_qto(obj, calculated_quantities) diff --git a/src/blenderbim/blenderbim/tool/qto.py b/src/blenderbim/blenderbim/tool/qto.py index 739b6e958a..8f8dad7d8d 100644 --- a/src/blenderbim/blenderbim/tool/qto.py +++ b/src/blenderbim/blenderbim/tool/qto.py @@ -24,7 +24,7 @@ import ifcopenshell 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 class Qto(blenderbim.core.tool.Qto): @@ -52,7 +52,8 @@ class Qto(blenderbim.core.tool.Qto): file = tool.Ifc.get() schema = file.schema pset_qto = util.pset.PsetQto(schema) - pset_qto_name = cls.get_pset_qto_name(obj) +# pset_qto_name = cls.get_pset_qto_name(obj) + pset_qto_name = cls.get_assigned_pset_qto_name(obj) pset_qto_properties = pset_qto.get_by_name(pset_qto_name).get_info()['HasPropertyTemplates'] return pset_qto_properties @@ -64,6 +65,13 @@ class Qto(blenderbim.core.tool.Qto): pset_qto_name = applicable_pset_name return pset_qto_name + @classmethod + def get_assigned_pset_qto_name(cls, obj): + entity = tool.Ifc.get_entity(obj) + assigned_pset_qto_name = ifcopenshell.util.element.get_psets(entity, qtos_only = True) + assigned_pset_qto_name = list(assigned_pset_qto_name.keys())[0] + return assigned_pset_qto_name + @classmethod def get_applicable_pset_names(cls, obj): file = tool.Ifc.get() @@ -77,8 +85,8 @@ class Qto(blenderbim.core.tool.Qto): @classmethod def edit_qto(cls, obj, calculated_quantities): file = tool.Ifc.get() - pset_qto_id = cls.get_pset_qto_id(obj) pset_qto_name = cls.get_pset_qto_name(obj) + pset_qto_id = cls.get_pset_qto_id(obj, pset_qto_name) ifcopenshell.api.run("pset.edit_qto", file, @@ -86,9 +94,8 @@ class Qto(blenderbim.core.tool.Qto): ) @classmethod - def get_pset_qto_id(cls, obj): + def get_pset_qto_id(cls, obj, pset_qto_name): file = tool.Ifc.get() - pset_qto_name = cls.get_pset_qto_name(obj) pset_qto_object_ifc_info = cls.get_pset_qto_object_ifc_info(obj) pset_qto_id = file.by_id(pset_qto_object_ifc_info[pset_qto_name]['id']) return pset_qto_id @@ -102,10 +109,15 @@ class Qto(blenderbim.core.tool.Qto): return pset_qto_name @classmethod - def get_new_quantity(cls, obj, quantity_name, alternative_prop_names): + 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 + + @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 new_quantity @classmethod def get_rounded_value(cls, new_quantity): @@ -113,12 +125,34 @@ class Qto(blenderbim.core.tool.Qto): @classmethod def get_calculated_quantities(cls, obj, pset_qto_properties): + calculated_quantities = {} + qto_name = cls.get_pset_qto_name(obj) + calculator = QtoCalculator() + + for pset_qto_property in pset_qto_properties: + quantity_name = pset_qto_property.get_info()['Name'] + 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 + def has_calculator(cls, qto_name, quantity_name): + return bool(mapper.get(qto_name, {}).get(quantity_name, None)) + + @classmethod + 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] - new_quantity = cls.get_new_quantity(obj, quantity_name, alternative_prop_names) + new_quantity = cls.get_new_guessed_quantity(obj, quantity_name, alternative_prop_names) if not new_quantity: new_quantity = 0 @@ -129,6 +163,7 @@ class Qto(blenderbim.core.tool.Qto): return calculated_quantities + @classmethod def assign_pset_qto_to_selected_object(cls, obj): file = tool.Ifc.get()