diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 39a17db65c..a1a7adda0f 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -26,21 +26,27 @@ classes = ( operator.EnableEditingResource, operator.LoadResources, operator.AddResource, + operator.AddResourceQuantity, operator.EditResource, operator.RemoveResource, + operator.RemoveResourceQuantity, operator.LoadResourceProperties, operator.ExpandResource, operator.ContractResource, operator.AssignResource, operator.UnassignResource, operator.EnableEditingResourceTime, + operator.EnableEditingResourceQuantity, + operator.EnableEditingResourceBaseQuantity, operator.EnableEditingResourceCosts, operator.EnableEditingResourceCostValueFormula, operator.EnableEditingResourceCostValue, operator.EditResourceTime, + operator.EditResourceQuantity, operator.EditResourceCostValue, operator.EditResourceCostValueFormula, operator.DisableEditingResourceTime, + operator.DisableEditingResourceQuantity, operator.DisableEditingResourceCostValue, operator.CalculateResourceWork, prop.Resource, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 547dd64396..412101ef71 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -573,3 +573,107 @@ class EditResourceCostValue(bpy.types.Operator): return True if prop.name == "UnitBasisUnit": return True + + +class EnableEditingResourceBaseQuantity(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_base_quantity" + bl_label = "Enable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.active_resource_id = self.resource + props.editing_resource_type = "QUANTITY" + return {"FINISHED"} + + +class AddResourceQuantity(bpy.types.Operator): + bl_idname = "bim.add_resource_quantity" + bl_label = "Add Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + ifc_class: bpy.props.StringProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.add_resource_quantity", + self.file, + resource=self.file.by_id(self.resource), + ifc_class=self.ifc_class, + ) + Data.load(self.file) + return {"FINISHED"} + + +class RemoveResourceQuantity(bpy.types.Operator): + bl_idname = "bim.remove_resource_quantity" + bl_label = "Remove Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.remove_resource_quantity", + self.file, + resource=self.file.by_id(self.resource), + ) + Data.load(self.file) + return {"FINISHED"} + + +class EnableEditingResourceQuantity(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_quantity" + bl_label = "Enable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMResourceProperties + self.props.quantity_attributes.clear() + self.props.is_editing_quantity = True + data = Data.resources[self.resource]["BaseQuantity"] + blenderbim.bim.helper.import_attributes(data["type"], self.props.quantity_attributes, data) + return {"FINISHED"} + + +class DisableEditingResourceQuantity(bpy.types.Operator): + bl_idname = "bim.disable_editing_resource_quantity" + bl_label = "Disable Editing Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + props = context.scene.BIMResourceProperties + props.is_editing_quantity = False + return {"FINISHED"} + + +class EditResourceQuantity(bpy.types.Operator): + bl_idname = "bim.edit_resource_quantity" + bl_label = "Edit Resource Quantity" + bl_options = {"REGISTER", "UNDO"} + physical_quantity: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMResourceProperties + attributes = blenderbim.bim.helper.export_attributes(props.quantity_attributes) + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.edit_resource_quantity", + self.file, + **{"physical_quantity": self.file.by_id(self.physical_quantity), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_resource_quantity() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py index f9f436f6f4..178171cd37 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/prop.py +++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py @@ -1,4 +1,3 @@ - # BlenderBIM Add-on - OpenBIM Blender Add-on # Copyright (C) 2020, 2021 Dion Moult # @@ -35,6 +34,14 @@ from bpy.props import ( ) +quantitytypes_enum = [] + + +def purge(): + global quantitytypes_enum + quantitytypes_enum = [] + + def updateResourceName(self, context): props = context.scene.BIMResourceProperties if not props.is_resource_update_enabled or self.name == "Unnamed": @@ -51,6 +58,18 @@ def updateResourceName(self, context): attribute.string_value = self.name +def get_quantity_types(self, context): + global quantitytypes_enum + if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): + quantitytypes_enum.extend( + [ + (t.name(), t.name(), "") + for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes() + ] + ) + return quantitytypes_enum + + class Resource(PropertyGroup): name: StringProperty(name="Name", update=updateResourceName) ifc_definition_id: IntProperty(name="IFC Definition ID") @@ -87,3 +106,6 @@ class BIMResourceProperties(PropertyGroup): cost_value_editing_type: StringProperty(name="Cost Value Editing Type") cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute) cost_value_formula: StringProperty(name="Cost Value Formula") + quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") + is_editing_quantity: BoolProperty(name="Is Editing Quantity") + quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index 730133be72..6a73a7cf0a 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -64,6 +64,8 @@ class BIM_PT_resources(Panel): if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES": self.draw_editable_resource_attributes_ui() + elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY": + self.draw_editable_resource_quantity_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS": self.draw_editable_resource_costs_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": @@ -111,6 +113,8 @@ class BIM_PT_resources(Panel): row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "COSTS": row.operator("bim.disable_editing_resource", text="", icon="CANCEL") + elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "QUANTITY": + row.operator("bim.disable_editing_resource", text="", icon="CANCEL") elif self.props.active_resource_id: row.operator("bim.add_resource", text="", icon="ADD").resource = ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id @@ -119,6 +123,8 @@ class BIM_PT_resources(Panel): op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") op.resource = ifc_definition_id row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id + op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES") + op.resource = ifc_definition_id op = row.operator("bim.enable_editing_resource_costs", text="", icon="DISC") op.resource = ifc_definition_id row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id @@ -130,6 +136,36 @@ class BIM_PT_resources(Panel): def draw_editable_resource_time_attributes_ui(self): blenderbim.bim.helper.draw_attributes(self.props.resource_time_attributes, self.layout) + def draw_editable_resource_quantity_ui(self): + resource = Data.resources[self.props.active_resource_id] + + if resource["BaseQuantity"]: + quantity = resource["BaseQuantity"] + value = quantity[[k for k in quantity.keys() if "Value" in k][0]] + row = self.layout.row(align=True) + row.label(text=quantity["Name"]) + row.label(text="{0:.2f}".format(value)) + if self.props.is_editing_quantity: + op = row.operator("bim.edit_resource_quantity", text="", icon="CHECKMARK") + op.physical_quantity = quantity["id"] + row.operator("bim.disable_editing_resource_quantity", text="", icon="CANCEL") + else: + op = row.operator("bim.enable_editing_resource_quantity", text="", icon="GREASEPENCIL") + op.resource = self.props.active_resource_id + op = row.operator("bim.remove_resource_quantity", text="", icon="X") + op.resource = self.props.active_resource_id + + if self.props.is_editing_quantity: + box = self.layout.box() + blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, box) + else: + row = self.layout.row(align=True) + row.prop(self.props, "quantity_types", text="") + op = row.operator("bim.add_resource_quantity", text="", icon="ADD") + op.resource = self.props.active_resource_id + op.ifc_class = self.props.quantity_types + + def draw_editable_resource_costs_ui(self): row = self.layout.row(align=True) row.prop(self.props, "cost_types", text="") diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py new file mode 100644 index 0000000000..6549e6d92c --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -0,0 +1,18 @@ +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None, "ifc_class": "IfcQuantityCount"} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + old_quantity = self.settings["resource"].BaseQuantity + self.settings["resource"].BaseQuantity = quantity + if old_quantity: + ifcopenshell.util.element.remove_deep(self.file, old_quantity) + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py index fae5c37779..462e310840 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py @@ -46,7 +46,8 @@ class Data(CostValueTrait): data["Usage"] = data["Usage"].id() data["TotalCostQuantity"] = cls.get_total_quantity(resource) if resource.BaseQuantity: - data["BaseQuantity"] = resource.BaseQuantity.id() + data["BaseQuantity"] = resource.BaseQuantity.get_info() + del data["BaseQuantity"]["Unit"] if resource.BaseCosts: data["BaseCosts"] = [e.id() for e in resource.BaseCosts] cls.load_cost_values(resource, data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py new file mode 100644 index 0000000000..bbf845d1f7 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"physical_quantity": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + setattr(self.settings["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py new file mode 100644 index 0000000000..ef16cc5669 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py @@ -0,0 +1,15 @@ +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + old_quantity = self.settings["resource"].BaseQuantity + self.settings["resource"].BaseQuantity = None + if old_quantity: + ifcopenshell.util.element.remove_deep(self.file, old_quantity)