diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 511fa5d248..196d6e1ad6 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -63,8 +63,6 @@ if bpy is not None: operator.SelectSweptSolidInnerCurves, operator.AssignSweptSolidExtrusion, operator.SelectSweptSolidExtrusion, - operator.AddQto, - operator.RemoveQto, operator.AddMaterialPset, operator.RemoveMaterialPset, operator.AddMaterialLayer, @@ -159,7 +157,6 @@ if bpy is not None: operator.PropagateTextData, operator.ConvertLocalToGlobal, operator.ConvertGlobalToLocal, - operator.GuessQuantity, operator.ExecuteBIMTester, operator.BIMTesterPurge, operator.SelectIfcPatchInput, @@ -270,7 +267,6 @@ if bpy is not None: ui.BIM_PT_material, ui.BIM_PT_presentation_layer_data, ui.BIM_PT_object_material, - ui.BIM_PT_object_qto, ui.BIM_PT_classification_references, ui.BIM_PT_documents, ui.BIM_PT_constraint_relations, diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index 5c202bc7da..7e9b061fd4 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -975,7 +975,6 @@ class IfcImporter: self.add_element_classifications(element, obj) self.add_element_document_relations(element, obj) self.add_opening_relation(element, obj) - self.add_product_definitions(element, obj) self.added_data[element.GlobalId] = obj if element.is_a("IfcOpeningElement"): @@ -1355,15 +1354,6 @@ class IfcImporter: bpy.ops.mesh.normals_make_consistent(context_override) bpy.ops.object.editmode_toggle(context_override) - def add_product_definitions(self, element, obj): - if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy: - return - for definition in element.IsDefinedBy: - if not definition.is_a("IfcRelDefinesByProperties"): - continue - if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"): - self.add_qto(definition.RelatingPropertyDefinition, obj) - def add_pset(self, pset, props): new_pset = props.psets.add() new_pset.name = pset.Name @@ -1392,27 +1382,6 @@ class IfcImporter: new_prop.name = prop.Name new_prop.string_value = str(prop.NominalValue.wrappedValue) - def add_qto(self, qto, obj): - new_qto = obj.BIMObjectProperties.qtos.add() - new_qto.name = str(qto.Name) - qto_template = schema.ifc.psetqto.get_by_name(new_qto.name) - if qto_template: - for prop_name in (p.Name for p in qto_template.HasPropertyTemplates): - prop = new_qto.properties.add() - prop.name = prop_name - for prop in qto.Quantities: - if prop.is_a("IfcPhysicalSimpleQuantity"): - value = getattr(prop, "{}Value".format(prop.is_a()[len("IfcQuantity") :])) - if not value: - continue - index = new_qto.properties.find(prop.Name) - if index >= 0: - new_qto.properties[index].string_value = str(value) - else: - new_prop = new_qto.properties.add() - new_prop.name = prop.Name - new_prop.string_value = str(value) - def add_opening_relation(self, element, obj): if not element.is_a("IfcOpeningElement"): return @@ -1708,7 +1677,6 @@ class IfcImporter: self.add_element_classifications(element, obj) self.add_element_document_relations(element, obj) - self.add_product_definitions(element, obj) self.aggregates[element.GlobalId] = obj self.aggregate_collections[rel_aggregate.id()] = collection @@ -1769,7 +1737,6 @@ class IfcImporter: obj.matrix_world = self.get_element_matrix(element, mesh_name) self.add_element_classifications(element, obj) self.add_element_document_relations(element, obj) - self.add_product_definitions(element, obj) self.added_data[element.GlobalId] = obj def add_element_document_relations(self, element, obj): diff --git a/src/ifcblenderexport/blenderbim/bim/module/cobie/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/cobie/__init__.py index 98091992a3..46ec55b5b6 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/cobie/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/cobie/__init__.py @@ -10,11 +10,9 @@ classes = ( ) - def register(): bpy.types.Scene.COBieProperties = bpy.props.PointerProperty(type=prop.COBieProperties) def unregister(): del bpy.types.Scene.COBieProperties - diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py index 31724d5e7b..d4a42735be 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/__init__.py @@ -1,5 +1,5 @@ import bpy -from . import ui, operator +from . import ui, prop, operator classes = ( operator.TogglePsetExpansion, @@ -8,13 +8,17 @@ classes = ( operator.EditPset, operator.RemovePset, operator.AddPset, + operator.AddQto, + operator.GuessQuantity, + prop.PsetProperties, ui.BIM_PT_object_psets, + ui.BIM_PT_object_qtos, ) def register(): - pass + bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties) def unregister(): - pass + del bpy.types.Object.PsetProperties diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/add_qto.py b/src/ifcblenderexport/blenderbim/bim/module/pset/add_qto.py new file mode 100644 index 0000000000..5df089d98e --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/add_qto.py @@ -0,0 +1,25 @@ +import ifcopenshell +import blenderbim.bim.schema # TODO: refactor + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = {"product": None, "Name": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + if self.settings["product"].is_a("IfcObject"): + qto = self.file.create_entity( + "IfcElementQuantity", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"]} + ) + self.file.create_entity( + "IfcRelDefinesByProperties", + **{ + "GlobalId": ifcopenshell.guid.new(), + # TODO: owner history + "RelatedObjects": [self.settings["product"]], + "RelatingPropertyDefinition": qto, + } + ) diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/data.py b/src/ifcblenderexport/blenderbim/bim/module/pset/data.py index d936db0e50..5c350f56f6 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/data.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/data.py @@ -6,6 +6,7 @@ from blenderbim.bim.ifc import IfcStore class Data: products = {} psets = {} + qtos = {} @classmethod def load(cls, product_id): @@ -13,7 +14,7 @@ class Data: if not file: return product = file.by_id(product_id) - cls.products[product_id] = set() + cls.products[product_id] = {"psets": set(), "qtos": set()} if product.is_a("IfcElementType"): cls.add_type_product_psets(product, product_id) else: @@ -36,47 +37,18 @@ class Data: continue if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"): cls.add_pset(definition.RelatingPropertyDefinition, product_id) + elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"): + cls.add_qto(definition.RelatingPropertyDefinition, product_id) @classmethod def add_pset(cls, pset, product_id): new_pset = { "Name": pset.Name, "is_expanded": True, - "Properties": [] + "Properties": cls.get_properties_from_template(pset.Name) or [] } - cls.products[product_id].add(int(pset.id())) + cls.products[product_id]["psets"].add(int(pset.id())) cls.psets[int(pset.id())] = new_pset - pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(pset.Name) - if pset_template: - for prop_template in pset_template.HasPropertyTemplates: - if not prop_template.is_a("IfcSimplePropertyTemplate"): - continue # Other types not yet supported - enum_items = [] - if prop_template.TemplateType == "P_SINGLEVALUE": - data_type = str(IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType)) - elif prop_template.TemplateType == "P_ENUMERATEDVALUE": - data_type = "enum" - enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues] - else: - continue # Other types not yet supported - if "" in data_type: - data_type = "string" - elif "" in data_type: - data_type = "float" - elif "" in data_type: - data_type = "integer" - elif "" in data_type: - data_type = "boolean" - elif "" in data_type: + data_type = "string" + elif "" in data_type: + data_type = "float" + elif "" in data_type: + data_type = "integer" + elif "" in data_type: + data_type = "boolean" + elif " 0: props.properties.remove(0) - props.active_pset_name = Data.psets[self.pset_id]["Name"] - for prop in Data.psets[self.pset_id]["Properties"]: + data = Data.psets if self.pset_id in Data.psets else Data.qtos + props.active_pset_name = data[self.pset_id]["Name"] + for prop in data[self.pset_id]["Properties"]: new = props.properties.add() new.name = prop["Name"] new.is_null = prop["is_null"] @@ -59,7 +64,7 @@ class DisablePsetEditing(bpy.types.Operator): def execute(self, context): obj = bpy.context.active_object - props = obj.BIMObjectProperties + props = obj.PsetProperties props.active_pset_id = 0 return {"FINISHED"} @@ -71,9 +76,11 @@ class EditPset(bpy.types.Operator): def execute(self, context): self.file = IfcStore.get_file() obj = bpy.context.active_object - props = obj.BIMObjectProperties + oprops = obj.BIMObjectProperties + props = obj.PsetProperties properties = {} - for prop in Data.psets[props.active_pset_id]["Properties"]: + data = Data.psets if props.active_pset_id in Data.psets else Data.qtos + for prop in data[props.active_pset_id]["Properties"]: blender_prop = props.properties.get(prop["Name"]) if not blender_prop: continue @@ -89,12 +96,19 @@ class EditPset(bpy.types.Operator): properties[prop["Name"]] = blender_prop.float_value elif prop["type"] == "enum": properties[prop["Name"]] = blender_prop.enum_value - edit_pset.Usecase(self.file, { - "pset": self.file.by_id(props.active_pset_id), - "Name": props.active_pset_name, - "Properties": properties - }).execute() - Data.load(props.ifc_definition_id) + if props.active_pset_id in Data.psets: + edit_pset.Usecase(self.file, { + "pset": self.file.by_id(props.active_pset_id), + "Name": props.active_pset_name, + "Properties": properties + }).execute() + else: + edit_qto.Usecase(self.file, { + "qto": self.file.by_id(props.active_pset_id), + "Name": props.active_pset_name, + "Properties": properties + }).execute() + Data.load(oprops.ifc_definition_id) bpy.ops.bim.disable_pset_editing() return {"FINISHED"} @@ -123,10 +137,72 @@ class AddPset(bpy.types.Operator): def execute(self, context): self.file = IfcStore.get_file() obj = bpy.context.active_object - props = obj.BIMObjectProperties + oprops = obj.BIMObjectProperties + props = obj.PsetProperties add_pset.Usecase(self.file, { - "product": self.file.by_id(props.ifc_definition_id), + "product": self.file.by_id(oprops.ifc_definition_id), "Name": props.pset_name, }).execute() - Data.load(props.ifc_definition_id) + Data.load(oprops.ifc_definition_id) return {"FINISHED"} + + +class AddQto(bpy.types.Operator): + bl_idname = "bim.add_qto" + bl_label = "Add Qto" + + def execute(self, context): + self.file = IfcStore.get_file() + obj = bpy.context.active_object + oprops = obj.BIMObjectProperties + props = obj.PsetProperties + add_qto.Usecase(self.file, { + "product": self.file.by_id(oprops.ifc_definition_id), + "Name": props.qto_name, + }).execute() + Data.load(oprops.ifc_definition_id) + return {"FINISHED"} + + +class GuessQuantity(bpy.types.Operator): + bl_idname = "bim.guess_quantity" + bl_label = "Guess Quantity" + prop: bpy.props.StringProperty() + + def execute(self, context): + self.qto_calculator = QtoCalculator() + obj = bpy.context.active_object + prop = obj.PsetProperties.properties.get(self.prop) + prop.float_value = self.guess_quantity(obj) + return {"FINISHED"} + + def guess_quantity(self, obj): + quantity = self.qto_calculator.guess_quantity(self.prop, [p.name for p in obj.PsetProperties.properties], obj) + if "area" in self.prop.lower(): + if bpy.context.scene.BIMProperties.area_unit: + prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.area_unit) + quantity = ifcopenshell.util.unit.convert(quantity, None, "SQUARE_METRE", prefix, name) + elif "volume" in self.prop.lower(): + if bpy.context.scene.BIMProperties.volume_unit: + prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.volume_unit) + quantity = ifcopenshell.util.unit.convert(quantity, None, "CUBIC_METRE", prefix, name) + else: + prefix, name = self.get_blender_prefix_name() + quantity = ifcopenshell.util.unit.convert(quantity, None, "METRE", prefix, name) + return round(quantity, 3) + + def get_prefix_name(self, value): + if "/" in value: + return value.split("/") + return None, value + + def get_blender_prefix_name(self): + if bpy.context.scene.unit_settings.system == "IMPERIAL": + if bpy.context.scene.unit_settings.length_unit == "INCHES": + return None, "inch" + elif bpy.context.scene.unit_settings.length_unit == "FEET": + return None, "foot" + elif bpy.context.scene.unit_settings.system == "METRIC": + if bpy.context.scene.unit_settings.length_unit == "METERS": + return None, "METRE" + return bpy.context.scene.unit_settings.length_unit[0 : -len("METERS")], "METRE" diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py b/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py new file mode 100644 index 0000000000..8762af706c --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/prop.py @@ -0,0 +1,48 @@ +import bpy +from blenderbim.bim.prop import Attribute +import blenderbim.bim.schema +from bpy.types import PropertyGroup +from bpy.props import ( + PointerProperty, + StringProperty, + EnumProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + CollectionProperty, +) + + +psetnames = {} +qtonames = {} + + +def getPsetNames(self, context): + global psetnames + if "/" in context.active_object.name: + ifc_class = context.active_object.name.split("/")[0] + if ifc_class not in psetnames: + psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True) + psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets] + return psetnames[ifc_class] + return [] + + +def getQtoNames(self, context): + global qtonames + if "/" in context.active_object.name: + ifc_class = context.active_object.name.split("/")[0] + if ifc_class not in qtonames: + psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True) + qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets] + return qtonames[ifc_class] + return [] + + +class PsetProperties(PropertyGroup): + active_pset_id: IntProperty(name="Active Pset ID") + active_pset_name: StringProperty(name="Pset Name") + properties: CollectionProperty(name="Properties", type=Attribute) + pset_name: EnumProperty(items=getPsetNames, name="Pset Name") + qto_name: EnumProperty(items=getQtoNames, name="Qto Name") diff --git a/src/ifcblenderexport/blenderbim/bim/qto.py b/src/ifcblenderexport/blenderbim/bim/module/pset/qto_calculator.py similarity index 100% rename from src/ifcblenderexport/blenderbim/bim/qto.py rename to src/ifcblenderexport/blenderbim/bim/module/pset/qto_calculator.py diff --git a/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py b/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py index 43411fa14b..37b8567583 100644 --- a/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/module/pset/ui.py @@ -1,6 +1,64 @@ from bpy.types import Panel from blenderbim.bim.module.pset.data import Data +def draw_psetqto_ui(pset_id, pset, props, layout): + box = layout.box() + row = box.row(align=True) + icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT" + row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id + if not props.active_pset_id: + row.label(text=pset["Name"], icon="COPY_ID") + row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id + row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id + elif props.active_pset_id != pset_id: + row.label(text=pset["Name"], icon="COPY_ID") + row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id + elif props.active_pset_id == pset_id: + row.prop(props, "active_pset_name", icon="COPY_ID", text="") + row.operator("bim.edit_pset", icon="CHECKMARK", text="") + row.operator("bim.disable_pset_editing", icon="X", text="") + if pset["is_expanded"]: + if props.active_pset_id == pset_id: + for prop in pset["Properties"]: + draw_psetqto_editable_ui(box, props, prop) + else: + for prop in pset["Properties"]: + if prop["value"] is None or prop["value"] == "": + continue + row = box.row(align=True) + row.scale_y = 0.8 + row.label(text=prop["Name"]) + row.label(text=str(prop["value"])) + +def draw_psetqto_editable_ui(box, props, prop): + row = box.row(align=True) + blender_prop = props.properties.get(prop["Name"]) + if prop["type"] == "string": + row.prop(blender_prop, "string_value", text=prop["Name"]) + elif prop["type"] == "integer": + row.prop(blender_prop, "int_value", text=prop["Name"]) + elif prop["type"] == "float": + row.prop(blender_prop, "float_value", text=prop["Name"]) + elif prop["type"] == "boolean": + row.prop(blender_prop, "bool_value", text=prop["Name"]) + elif prop["type"] == "enum": + row.prop(blender_prop, "enum_value", text=prop["Name"]) + row.prop(blender_prop, "is_null", icon="RADIOBUT_OFF" if blender_prop.is_null else "RADIOBUT_ON", text="") + if ( + "length" in prop["Name"].lower() + or "width" in prop["Name"].lower() + or "height" in prop["Name"].lower() + or "depth" in prop["Name"].lower() + or "perimeter" in prop["Name"].lower() + ): + op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="") + op.prop = prop["Name"] + elif "area" in prop["Name"].lower(): + op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="") + op.prop = prop["Name"] + elif "volume" in prop["Name"].lower(): + op = row.operator("bim.guess_quantity", icon="SPHERE", text="") + op.prop = prop["Name"] class BIM_PT_object_psets(Panel): bl_label = "IFC Object Property Sets" @@ -23,61 +81,57 @@ class BIM_PT_object_psets(Panel): return True def draw(self, context): - props = context.active_object.BIMObjectProperties - if not props.ifc_definition_id: + oprops = context.active_object.BIMObjectProperties + props = context.active_object.PsetProperties + if not oprops.ifc_definition_id: return - if props.ifc_definition_id not in Data.products: - Data.load(props.ifc_definition_id) + if oprops.ifc_definition_id not in Data.products: + Data.load(oprops.ifc_definition_id) row = self.layout.row(align=True) row.prop(props, "pset_name", text="") row.operator("bim.add_pset", icon="ADD", text="") - self.draw_psets_ui(props, Data.products[props.ifc_definition_id]) + for pset_id in Data.products[oprops.ifc_definition_id]["psets"]: + pset = Data.psets[pset_id] + draw_psetqto_ui(pset_id, pset, props, self.layout) # TODO reimplement. See #1222. - #if props.relating_type and props.relating_type.BIMObjectProperties.psets: + #if props.relating_type and props.relating_type.PsetProperties.psets: # self.layout.label(text="Inherited Psets:") - # self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False) + # self.draw_psets_ui(props.relating_type.PsetProperties, enabled=False) - def draw_psets_ui(self, props, pset_ids): - for pset_id in pset_ids: - pset = Data.psets[pset_id] - box = self.layout.box() - row = box.row(align=True) - icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT" - row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id - if not props.active_pset_id: - row.label(text=pset["Name"], icon="COPY_ID") - row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id - row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id - elif props.active_pset_id != pset_id: - row.label(text=pset["Name"], icon="COPY_ID") - row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id - elif props.active_pset_id == pset_id: - row.prop(props, "active_pset_name", icon="COPY_ID", text="") - row.operator("bim.edit_pset", icon="CHECKMARK", text="") - row.operator("bim.disable_pset_editing", icon="X", text="") - if pset["is_expanded"]: - if props.active_pset_id == pset_id: - for prop in pset["Properties"]: - row = box.row(align=True) - blender_prop = props.properties.get(prop["Name"]) - if prop["type"] == "string": - row.prop(blender_prop, "string_value", text=prop["Name"]) - elif prop["type"] == "integer": - row.prop(blender_prop, "int_value", text=prop["Name"]) - elif prop["type"] == "float": - row.prop(blender_prop, "float_value", text=prop["Name"]) - elif prop["type"] == "boolean": - row.prop(blender_prop, "bool_value", text=prop["Name"]) - elif prop["type"] == "enum": - row.prop(blender_prop, "enum_value", text=prop["Name"]) - row.prop(blender_prop, "is_null", icon="RADIOBUT_OFF" if blender_prop.is_null else "RADIOBUT_ON", text="") - else: - for prop in pset["Properties"]: - if prop["value"] is None or prop["value"] == "": - continue - row = box.row(align=True) - row.scale_y = 0.8 - row.label(text=prop["Name"]) - row.label(text=str(prop["value"])) + +class BIM_PT_object_qtos(Panel): + bl_label = "IFC Object Quantity Sets" + bl_idname = "BIM_PT_object_qtos" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + if not context.active_object: + return False + props = context.active_object.BIMObjectProperties + if not props.ifc_definition_id: + return False + if props.ifc_definition_id not in Data.products: + Data.load(props.ifc_definition_id) + if not Data.products[props.ifc_definition_id]: + return False + return True + + def draw(self, context): + oprops = context.active_object.BIMObjectProperties + props = context.active_object.PsetProperties + if not oprops.ifc_definition_id: + return + if oprops.ifc_definition_id not in Data.products: + Data.load(oprops.ifc_definition_id) + row = self.layout.row(align=True) + row.prop(props, "qto_name", text="") + row.operator("bim.add_qto", icon="ADD", text="") + + for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]: + qto = Data.qtos[qto_id] + draw_psetqto_ui(qto_id, qto, props, self.layout) diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index e684639e3f..a5c284529d 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -105,7 +105,7 @@ class ExportIFC(bpy.types.Operator): else: output_file = bpy.path.ensure_ext(self.filepath, ".ifc") ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger) - qto_calculator = qto.QtoCalculator() + qto_calculator = None # TODO: remove from export ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator) ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser) ifc_export_settings.logger.info("Starting export") @@ -2805,70 +2805,6 @@ class ConvertGlobalToLocal(bpy.types.Operator): return {"FINISHED"} -class GuessQuantity(bpy.types.Operator): - bl_idname = "bim.guess_quantity" - bl_label = "Guess Quantity" - qto_index: bpy.props.IntProperty() - prop_index: bpy.props.IntProperty() - - def execute(self, context): - self.qto_calculator = qto.QtoCalculator() - source_qto = bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index] - props = source_qto.properties - prop = props[self.prop_index] - for obj in bpy.context.selected_objects: - dest_qto = obj.BIMObjectProperties.qtos.get(source_qto.name) - if not dest_qto: - if source_qto.name not in obj.BIMObjectProperties.qto_name: - continue - dest_qto = self.add_qto(obj, source_qto.name) - prop = dest_qto.properties.get(prop.name) - self.guess_quantity(obj, prop, props) - return {"FINISHED"} - - def guess_quantity(self, obj, prop, props): - quantity = self.qto_calculator.guess_quantity(prop.name, [p.name for p in props], obj) - if "area" in prop.name.lower(): - if bpy.context.scene.BIMProperties.area_unit: - prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.area_unit) - quantity = helper.SIUnitHelper.convert(quantity, None, "SQUARE_METRE", prefix, name) - elif "volume" in prop.name.lower(): - if bpy.context.scene.BIMProperties.volume_unit: - prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.volume_unit) - quantity = helper.SIUnitHelper.convert(quantity, None, "CUBIC_METRE", prefix, name) - else: - prefix, name = self.get_blender_prefix_name() - quantity = helper.SIUnitHelper.convert(quantity, None, "METRE", prefix, name) - prop.string_value = str(round(quantity, 3)) - - def add_qto(self, obj, name): - qto_template = schema.ifc.psetqto.get_by_name(name) - if not qto_template: - return - qto = obj.BIMObjectProperties.qtos.add() - qto.name = name - for prop_name in (p.Name for p in qto_template.HasPropertyTemplates): - prop = qto.properties.add() - prop.name = prop_name - return qto - - def get_prefix_name(self, value): - if "/" in value: - return value.split("/") - return None, value - - def get_blender_prefix_name(self): - if bpy.context.scene.unit_settings.system == "IMPERIAL": - if bpy.context.scene.unit_settings.length_unit == "INCHES": - return None, "inch" - elif bpy.context.scene.unit_settings.length_unit == "FEET": - return None, "foot" - elif bpy.context.scene.unit_settings.system == "METRIC": - if bpy.context.scene.unit_settings.length_unit == "METERS": - return None, "METRE" - return bpy.context.scene.unit_settings.length_unit[0 : -len("METERS")], "METRE" - - class ExecuteBIMTester(bpy.types.Operator): bl_idname = "bim.execute_bim_tester" bl_label = "Execute BIMTester" @@ -2982,6 +2918,7 @@ class CalculateObjectVolumes(bpy.types.Operator): bl_label = "Calculate Object Volumes" def execute(self, context): + # TODO: reimplement qto_calculator = qto.QtoCalculator() result = 0 for obj in bpy.context.selected_objects: diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index 5b7540b865..7f06227817 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -40,8 +40,6 @@ psettemplatefiles_enum = [] propertysettemplates_enum = [] classification_enum = [] attributes_enum = [] -psetnames = {} -qtonames_enum = [] materialattributes_enum = [] materialtypes_enum = [] contexts_enum = [] @@ -445,17 +443,6 @@ def refreshReferences(self, context): context.scene.BIMProperties.classification_references.root = "" -def getPsetNames(self, context): - global psetnames - if "/" in context.active_object.name: - ifc_class = context.active_object.name.split("/")[0] - if ifc_class not in psetnames: - psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True) - psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets] - return psetnames[ifc_class] - return [] - - def getMaterialPsetNames(self, context): global materialpsetnames_enum materialpsetnames_enum.clear() @@ -464,15 +451,6 @@ def getMaterialPsetNames(self, context): return materialpsetnames_enum -def getQtoNames(self, context): - global qtonames_enum - qtonames_enum.clear() - if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements: - qto_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], qto_only=True) - qtonames_enum.extend([(q, q, "") for q in qto_names]) - return qtonames_enum - - def getApplicableMaterialAttributes(self, context): global materialattributes_enum materialattributes_enum.clear() @@ -1492,9 +1470,6 @@ class BIMObjectProperties(PropertyGroup): is_editing_type: BoolProperty(name="Is Editing Type") relating_type: PointerProperty(name="Type Product", type=bpy.types.Object) relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object) - active_pset_id: IntProperty(name="Active Pset ID") - active_pset_name: StringProperty(name="Pset Name") - properties: CollectionProperty(name="Properties", type=Attribute) psets: CollectionProperty(name="Psets", type=PsetQto) qtos: CollectionProperty(name="Qtos", type=PsetQto) document_references: CollectionProperty(name="Document References", type=DocumentReference) @@ -1505,8 +1480,6 @@ class BIMObjectProperties(PropertyGroup): material_type: EnumProperty(items=getMaterialTypes, name="Material Type") material: PointerProperty(name="Material", type=bpy.types.Material) material_set: PointerProperty(name="Material Set", type=MaterialSet) - pset_name: EnumProperty(items=getPsetNames, name="Pset Name") - qto_name: EnumProperty(items=getQtoNames, name="Qto Name") has_boundary_condition: BoolProperty(name="Has Boundary Condition") boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition) structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object) diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index d4e843a38e..0adcd72b36 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -143,61 +143,6 @@ class BIM_PT_object_material(Panel): row.prop(attribute, "string_value", text="") -class BIM_PT_object_qto(Panel): - bl_label = "IFC Object Quantity Sets" - bl_idname = "BIM_PT_object_qto" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "object" - - @classmethod - def poll(cls, context): - return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties") - - def draw(self, context): - if context.active_object is None: - return - layout = self.layout - props = context.active_object.BIMObjectProperties - row = layout.row(align=True) - row.prop(props, "qto_name", text="") - row.operator("bim.add_qto") - - for index, qto in enumerate(props.qtos): - box = self.layout.box() - row = box.row(align=True) - - row.prop( - qto, "is_expanded", icon="TRIA_DOWN" if qto.is_expanded else "TRIA_RIGHT", icon_only=True, emboss=False - ) - row.prop(qto, "name", text="", icon="COPY_ID") - row.operator("bim.remove_qto", icon="X", text="").index = index - if not qto.is_expanded: - continue - for index2, prop in enumerate(qto.properties): - row = box.row(align=True) - row.prop(prop, "name", text="") - row.prop(prop, "string_value", text="") - if ( - "length" in prop.name.lower() - or "width" in prop.name.lower() - or "height" in prop.name.lower() - or "depth" in prop.name.lower() - or "perimeter" in prop.name.lower() - ): - op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="") - op.qto_index = index - op.prop_index = index2 - elif "area" in prop.name.lower(): - op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="") - op.qto_index = index - op.prop_index = index2 - elif "volume" in prop.name.lower(): - op = row.operator("bim.guess_quantity", icon="SPHERE", text="") - op.qto_index = index - op.prop_index = index2 - - class BIM_PT_object_structural(Panel): bl_label = "IFC Structural Relationships" bl_idname = "BIM_PT_object_structural"