diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 04d07e0386..b56998d388 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -46,7 +46,7 @@ def draw_attribute(attribute, layout, copy_operator=None): layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") if copy_operator: op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN") - op.data = json.dumps({"name": attribute.name, "value": attribute.get_value(), "is_null": attribute.is_null}) + op.attribute_name = attribute.name def import_attributes(ifc_class, props, data, callback=None): diff --git a/src/blenderbim/blenderbim/bim/module/attribute/data.py b/src/blenderbim/blenderbim/bim/module/attribute/data.py new file mode 100644 index 0000000000..ed0ccf906d --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/attribute/data.py @@ -0,0 +1,74 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 Dion Moult +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import ifcopenshell +import blenderbim.tool as tool + + +def refresh(): + AttributesData.is_loaded = False + MaterialAttributesData.is_loaded = False + + +class AttributesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"attributes": cls.attributes()} + cls.is_loaded = True + + @classmethod + def attributes(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object) + data = element.get_info() + if hasattr(element, "GlobalId"): + excluded_keys = ["id", "type"] + else: + excluded_keys = ["type"] + for key, value in data.items(): + if value is None or isinstance(value, ifcopenshell.entity_instance) or key in excluded_keys: + continue + if key == "id": + key = "STEP ID" + results.append({"name": key, "value": str(value)}) + return results + + +class MaterialAttributesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = {"attributes": cls.attributes()} + cls.is_loaded = True + + @classmethod + def attributes(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object.active_material) + data = element.get_info() + for key, value in data.items(): + if value is None or isinstance(value, ifcopenshell.entity_instance) or key in ["id", "type"]: + continue + results.append({"name": key, "value": str(value)}) + return results diff --git a/src/blenderbim/blenderbim/bim/module/attribute/operator.py b/src/blenderbim/blenderbim/bim/module/attribute/operator.py index f282b81eaf..d96d2aa57f 100644 --- a/src/blenderbim/blenderbim/bim/module/attribute/operator.py +++ b/src/blenderbim/blenderbim/bim/module/attribute/operator.py @@ -75,16 +75,13 @@ class DisableEditingAttributes(bpy.types.Operator): return {"FINISHED"} -class EditAttributes(bpy.types.Operator): +class EditAttributes(bpy.types.Operator, Operator): bl_idname = "bim.edit_attributes" bl_label = "Edit Attributes" bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() obj_type: bpy.props.StringProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): self.file = IfcStore.get_file() if self.obj_type == "Object": diff --git a/src/blenderbim/blenderbim/bim/module/attribute/ui.py b/src/blenderbim/blenderbim/bim/module/attribute/ui.py index 7b3efc9483..8aa227e329 100644 --- a/src/blenderbim/blenderbim/bim/module/attribute/ui.py +++ b/src/blenderbim/blenderbim/bim/module/attribute/ui.py @@ -16,17 +16,16 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import blenderbim.bim.helper from bpy.types import Panel from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.attribute.data import Data +from blenderbim.bim.module.attribute.data import AttributesData, MaterialAttributesData -def draw_ui(context, layout, obj_type): +def draw_ui(context, layout, obj_type, attributes): obj = context.active_object if obj_type == "Object" else context.active_object.active_material oprops = obj.BIMObjectProperties props = obj.BIMAttributeProperties - if oprops.ifc_definition_id not in Data.products: - Data.load(IfcStore.get_file(), oprops.ifc_definition_id) if props.is_editing_attributes: row = layout.row(align=True) @@ -37,49 +36,17 @@ def draw_ui(context, layout, obj_type): op.obj_type = obj_type op.obj = obj.name - for attribute in Data.products[oprops.ifc_definition_id]: - if attribute["type"] == "entity": - continue - row = layout.row(align=True) - blender_attribute = props.attributes.get(attribute["name"]) - if attribute["type"] == "string" or attribute["type"] == "list": - row.prop(blender_attribute, "string_value", text=attribute["name"]) - elif attribute["type"] == "integer": - row.prop(blender_attribute, "int_value", text=attribute["name"]) - elif attribute["type"] == "float": - row.prop(blender_attribute, "float_value", text=attribute["name"]) - elif attribute["type"] == "enum": - row.prop(blender_attribute, "enum_value", text=attribute["name"]) - - if attribute["name"] == "GlobalId": - row.operator("bim.generate_global_id", icon="FILE_REFRESH", text="") - if attribute["is_optional"]: - row.prop( - blender_attribute, - "is_null", - icon="RADIOBUT_OFF" if blender_attribute.is_null else "RADIOBUT_ON", - text="", - ) - if attribute["name"] != "GlobalId": - op = row.operator("bim.copy_attribute_to_selection", icon="COPYDOWN", text="") - op.attribute_name = attribute["name"] + blenderbim.bim.helper.draw_attributes(props.attributes, layout, copy_operator="bim.copy_attribute_to_selection") else: row = layout.row() op = row.operator("bim.enable_editing_attributes", icon="GREASEPENCIL", text="Edit") op.obj_type = obj_type op.obj = obj.name - if "GlobalId" not in [a["name"] for a in Data.products[oprops.ifc_definition_id]]: - row = layout.row(align=True) - row.label(text="STEP ID") - row.label(text=str(oprops.ifc_definition_id)) - - for attribute in Data.products[oprops.ifc_definition_id]: - if attribute["value"] is None or attribute["type"] == "entity": - continue + for attribute in attributes: row = layout.row(align=True) row.label(text=attribute["name"]) - row.label(text=str(attribute["value"])) + row.label(text=attribute["value"]) # TODO: reimplement, see #1222 # if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name: @@ -102,7 +69,9 @@ class BIM_PT_object_attributes(Panel): return bool(context.active_object.BIMObjectProperties.ifc_definition_id) def draw(self, context): - draw_ui(context, self.layout, "Object") + if not AttributesData.is_loaded: + AttributesData.load() + draw_ui(context, self.layout, "Object", AttributesData.data["attributes"]) class BIM_PT_material_attributes(Panel): @@ -122,4 +91,6 @@ class BIM_PT_material_attributes(Panel): return False def draw(self, context): - draw_ui(context, self.layout, "Material") + if not MaterialAttributesData.is_loaded: + MaterialAttributesData.load() + draw_ui(context, self.layout, "Material", MaterialAttributesData.data["attributes"]) diff --git a/src/blenderbim/blenderbim/bim/module/model/grid.py b/src/blenderbim/blenderbim/bim/module/model/grid.py index 08b424bb6b..bc605a3116 100644 --- a/src/blenderbim/blenderbim/bim/module/model/grid.py +++ b/src/blenderbim/blenderbim/bim/module/model/grid.py @@ -18,6 +18,8 @@ import bpy import ifcopenshell.api +import blenderbim.core.spatial +import blenderbim.tool as tool from bpy.types import Operator from bpy.props import FloatProperty, IntProperty from mathutils import Vector @@ -51,8 +53,8 @@ def add_object(self, context): if has_site_collection: site_obj = bpy.data.objects.get(grandchild.name) if site_obj and site_obj.BIMObjectProperties.ifc_definition_id: - bpy.ops.bim.assign_container( - relating_structure=site_obj.BIMObjectProperties.ifc_definition_id, related_element=obj.name + blenderbim.core.spatial.assign_container( + tool.Ifc, tool.Collector, tool.Spatial, structure_obj=site_obj, element_obj=obj ) axes_collection = bpy.data.collections.new("UAxes") diff --git a/src/blenderbim/test/bim/feature/model.feature b/src/blenderbim/test/bim/feature/model.feature index dd4a870c1b..ce9b7f2c9b 100644 --- a/src/blenderbim/test/bim/feature/model.feature +++ b/src/blenderbim/test/bim/feature/model.feature @@ -26,3 +26,14 @@ Scenario: Add type instance - add from an empty And I set "scene.BIMTypeProperties.relating_type" to "{empty}" When I press "bim.add_type_instance" Then the object "IfcWall/Instance" exists + +Scenario: Add grid + Given an empty IFC project + When I press "mesh.add_grid" + Then the object "IfcGrid/Grid" is an "IfcGrid" + And the object "IfcGridAxis/A" is an "IfcGridAxis" + And the object "IfcGridAxis/B" is an "IfcGridAxis" + And the object "IfcGridAxis/C" is an "IfcGridAxis" + And the object "IfcGridAxis/01" is an "IfcGridAxis" + And the object "IfcGridAxis/02" is an "IfcGridAxis" + And the object "IfcGridAxis/03" is an "IfcGridAxis" diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py index c7cd97671a..324a16b8ec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py @@ -12,7 +12,7 @@ class Usecase: def execute(self): element = self.file.create_entity( - "IfcGridAxis", **{"axis_tag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]} + "IfcGridAxis", **{"AxisTag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]} ) axes = list(getattr(self.settings["grid"], self.settings["uvw_axes"]) or []) axes.append(element) diff --git a/src/ifcopenshell-python/test/api/grid/test_create_grid_axis.py b/src/ifcopenshell-python/test/api/grid/test_create_grid_axis.py new file mode 100644 index 0000000000..d6ec2bab62 --- /dev/null +++ b/src/ifcopenshell-python/test/api/grid/test_create_grid_axis.py @@ -0,0 +1,17 @@ +import test.bootstrap +import ifcopenshell.api + + +class TestCreateGridAxis(test.bootstrap.IFC4): + def test_run(self): + grid = self.file.createIfcGrid() + axis = ifcopenshell.api.run( + "grid.create_grid_axis", self.file, axis_tag="axis_tag", same_sense=True, uvw_axes="UAxes", grid=grid + ) + assert axis.AxisTag == "axis_tag" + assert axis.SameSense is True + assert grid.UAxes == (axis,) + axis2 = ifcopenshell.api.run( + "grid.create_grid_axis", self.file, axis_tag="axis_tag", same_sense=True, uvw_axes="UAxes", grid=grid + ) + assert grid.UAxes == (axis,axis2)