diff --git a/src/ifcblenderexport/features/steps/example_steps.py b/src/ifcblenderexport/features/steps/example_steps.py new file mode 100644 index 0000000000..a514985f48 --- /dev/null +++ b/src/ifcblenderexport/features/steps/example_steps.py @@ -0,0 +1,14 @@ +import ifcopenshell +from behave import given, when, then, step + +@given('the IFC file "{file}"') +def step_impl(context, file): + context.file = ifcopenshell.open(file) + +@then('the element {id} is an {ifc_class}') +def step_impl(context, id, ifc_class): + assert context.file.by_id(id).is_a(ifc_class) + +@then('the element {id} should not exist because {reason}') +def step_impl(context, id, reason): + assert not context.file.by_id(id) diff --git a/src/ifcblenderexport/io_export_ifc/__init__.py b/src/ifcblenderexport/io_export_ifc/__init__.py index 9a3b7a2a9f..f053872790 100644 --- a/src/ifcblenderexport/io_export_ifc/__init__.py +++ b/src/ifcblenderexport/io_export_ifc/__init__.py @@ -22,6 +22,12 @@ classes = ( operator.SelectSchemaDir, operator.ExportIFC, operator.ImportIFC, + operator.ColourByClass, + operator.ResetObjectColours, + operator.ApproveClass, + operator.RejectClass, + operator.SelectAudited, + operator.RejectElement, ui.BIMProperties, ui.BIMPanel, ui.MVDPanel, diff --git a/src/ifcblenderexport/io_export_ifc/operator.py b/src/ifcblenderexport/io_export_ifc/operator.py index 0a50d38ad5..21a98ae5bc 100644 --- a/src/ifcblenderexport/io_export_ifc/operator.py +++ b/src/ifcblenderexport/io_export_ifc/operator.py @@ -4,6 +4,7 @@ import logging from . import export_ifc from . import import_ifc from bpy_extras.io_utils import ImportHelper +from itertools import cycle class ExportIFC(bpy.types.Operator): bl_idname = "export.ifc" @@ -116,6 +117,90 @@ class SelectType(bpy.types.Operator): object.select_set(True) return {'FINISHED'} +class ColourByClass(bpy.types.Operator): + bl_idname = 'bim.colour_by_class' + bl_label = 'Colour by Class' + + def execute(self, context): + colour_list = [ + (.651, .81, .892, 1), + (.121, .471, .706, 1), + (.699, .876, .54, 1), + (.199, .629, .174, 1), + (.983, .605, .602, 1), + (.89, .101, .112, 1), + (.989, .751, .427, 1), + (.986, .497, .1, 1), + (.792, .699, .839, 1), + (.414, .239, .603, 1), + (.993, .999, .6, 1), + (.693, .349, .157, 1)] + colours = cycle(colour_list) + ifc_classes = {} + for object in bpy.context.selected_objects: + if '/' not in object.name: + continue + ifc_class = object.name.split('/')[0] + if ifc_class not in ifc_classes: + ifc_classes[ifc_class] = next(colours) + object.color = ifc_classes[ifc_class] + return {'FINISHED'} + +class ResetObjectColours(bpy.types.Operator): + bl_idname = 'bim.reset_object_colours' + bl_label = 'Reset Colours' + + def execute(self, context): + for object in bpy.context.selected_objects: + object.color = (1, 1, 1, 1) + return {'FINISHED'} + +class ApproveClass(bpy.types.Operator): + bl_idname = 'bim.approve_class' + bl_label = 'Approve Class' + + def execute(self, context): + with open(bpy.context.scene.BIMProperties.data_dir + 'audit.txt', 'a') as file: + for object in bpy.context.selected_objects: + file.write('Then the element {} is an {}\n'.format( + object['IfcGlobalId'], object.name.split('/')[0])) + return {'FINISHED'} + +class RejectClass(bpy.types.Operator): + bl_idname = 'bim.reject_class' + bl_label = 'Reject Class' + + def execute(self, context): + with open(bpy.context.scene.BIMProperties.data_dir + 'audit.txt', 'a') as file: + for object in bpy.context.selected_objects: + file.write('Then the element {} is an {}\n'.format( + object['IfcGlobalId'], bpy.context.scene.BIMProperties.ifc_class)) + return {'FINISHED'} + +class RejectElement(bpy.types.Operator): + bl_idname = 'bim.reject_element' + bl_label = 'Reject Element' + + def execute(self, context): + with open(bpy.context.scene.BIMProperties.data_dir + 'audit.txt', 'a') as file: + for object in bpy.context.selected_objects: + file.write('Then the element {} should not exist because {}\n'.format( + object['IfcGlobalId'], bpy.context.scene.BIMProperties.qa_reject_element_reason)) + return {'FINISHED'} + +class SelectAudited(bpy.types.Operator): + bl_idname = 'bim.select_audited' + bl_label = 'Select Audited' + + def execute(self, context): + with open(bpy.context.scene.BIMProperties.data_dir + 'audit.txt') as file: + for line in file: + for object in bpy.context.visible_objects: + if 'IfcGlobalId' in object.keys() \ + and object['IfcGlobalId'] == line.split(' ')[3]: + object.select_set(True) + return {'FINISHED'} + class SelectDataDir(bpy.types.Operator): bl_idname = "bim.select_data_dir" bl_label = "Select Data Directory" diff --git a/src/ifcblenderexport/io_export_ifc/ui.py b/src/ifcblenderexport/io_export_ifc/ui.py index 7cefa68037..bf4963a201 100644 --- a/src/ifcblenderexport/io_export_ifc/ui.py +++ b/src/ifcblenderexport/io_export_ifc/ui.py @@ -33,6 +33,7 @@ class BIMProperties(bpy.types.PropertyGroup): name="Predefined Type", default=None) ifc_userdefined_type: bpy.props.StringProperty(name="Userdefined Type") export_has_representations: bpy.props.BoolProperty(name="Export Representations", default=True) + qa_reject_element_reason: bpy.props.StringProperty(name="Element rejection reason") class BIMPanel(bpy.types.Panel): bl_label = "Building Information Modeling" @@ -74,6 +75,24 @@ class BIMPanel(bpy.types.Panel): row.operator("bim.select_class") row.operator("bim.select_type") + layout.label(text="Quality Auditing:") + + row = layout.row() + row.prop(bim_properties, "qa_reject_element_reason") + row = layout.row() + row.operator("bim.reject_element") + + row = layout.row(align=True) + row.operator("bim.colour_by_class") + row.operator("bim.reset_object_colours") + + row = layout.row(align=True) + row.operator("bim.approve_class") + row.operator("bim.reject_class") + + row = layout.row() + row.operator("bim.select_audited") + class MVDPanel(bpy.types.Panel): bl_label = "Model View Definitions" bl_idname = "mvd.panel"