diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 8cdddb8b88..97ec732645 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -16,6 +16,7 @@ if bpy is not None: "classification": None, "cobie": None, "context": None, + "constraint": None, "covetool": None, "csv": None, "diff": None, @@ -67,11 +68,6 @@ if bpy is not None: operator.AddMaterialProfile, operator.RemoveMaterialProfile, operator.MoveMaterialProfile, - operator.AddConstraint, - operator.RemoveConstraint, - operator.AssignConstraint, - operator.UnassignConstraint, - operator.RemoveObjectConstraint, operator.AddMaterialAttribute, operator.RemoveMaterialAttribute, operator.FetchLibraryInformation, @@ -166,7 +162,6 @@ if bpy is not None: prop.ClashSource, prop.ClashSet, prop.SmartClashGroup, - prop.Constraint, prop.Drawing, prop.Schedule, prop.DrawingStyle, @@ -192,14 +187,12 @@ if bpy is not None: ui.BIM_PT_schedules, ui.BIM_PT_sheets, ui.BIM_PT_psets, - ui.BIM_PT_constraints, ui.BIM_PT_ifcclash, ui.BIM_PT_library, ui.BIM_PT_presentation_layers, ui.BIM_PT_patch, ui.BIM_PT_mvd, ui.BIM_PT_presentation_layer_data, - ui.BIM_PT_constraint_relations, ui.BIM_PT_object_structural, ui.BIM_PT_camera, ui.BIM_PT_text, @@ -212,7 +205,6 @@ if bpy is not None: ui.BIM_UL_drawinglist, ui.BIM_UL_clash_sets, ui.BIM_UL_smart_groups, - ui.BIM_UL_constraints, ui.BIM_UL_topics, ui.BIM_ADDON_preferences, ] diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index 732f1396ae..33d1197eb0 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -349,8 +349,6 @@ class IfcImporter: self.profile_code("Set units") self.create_project() self.profile_code("Create project") - self.create_constraints() - self.profile_code("Create constraints") self.create_spatial_hierarchy() self.profile_code("Create spatial hierarchy") self.create_type_products() @@ -1245,22 +1243,6 @@ class IfcImporter: self.project["blender"].objects.link(obj) del self.added_data[self.project["ifc"].GlobalId] - def create_constraints(self): - for element in self.file.by_type("IfcObjective"): - constraint = bpy.context.scene.BIMProperties.constraints.add() - data_map = { - "name": "Name", - "description": "Description", - "constraint_grade": "ConstraintGrade", - "constraint_source": "ConstraintSource", - "user_defined_grade": "UserDefinedGrade", - "objective_qualifier": "ObjectiveQualifier", - "user_defined_qualifier": "UserDefinedQualifier", - } - for key, value in data_map.items(): - if hasattr(element, value) and getattr(element, value): - setattr(constraint, key, getattr(element, value)) - def create_spatial_hierarchy(self): if self.project["ifc"].IsDecomposedBy: for rel_aggregate in self.project["ifc"].IsDecomposedBy: diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/__init__.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/__init__.py new file mode 100644 index 0000000000..66e17b808d --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/__init__.py @@ -0,0 +1,33 @@ +import bpy +from . import ui, prop, operator + +classes = ( + operator.LoadObjectives, + operator.DisableConstraintEditingUI, + operator.EnableEditingConstraint, + operator.DisableEditingConstraint, + operator.AddObjective, + operator.EditObjective, + operator.RemoveConstraint, + operator.EnableAssigningConstraint, + operator.DisableAssigningConstraint, + operator.AssignConstraint, + operator.UnassignConstraint, + prop.Constraint, + prop.BIMConstraintProperties, + prop.BIMObjectConstraintProperties, + ui.BIM_PT_constraints, + ui.BIM_PT_object_constraints, + ui.BIM_UL_constraints, + ui.BIM_UL_object_constraints, +) + + +def register(): + bpy.types.Scene.BIMConstraintProperties = bpy.props.PointerProperty(type=prop.BIMConstraintProperties) + bpy.types.Object.BIMObjectConstraintProperties = bpy.props.PointerProperty(type=prop.BIMObjectConstraintProperties) + + +def unregister(): + del bpy.types.Scene.BIMConstraintProperties + del bpy.types.Scene.BIMObjectConstraintProperties diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/add_objective.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/add_objective.py new file mode 100644 index 0000000000..5891bfb49d --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/add_objective.py @@ -0,0 +1,15 @@ +import ifcopenshell + +class Usecase: + def __init__(self, file, settings={}): + self.file = file + self.settings = {} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + return self.file.create_entity("IfcObjective", **{ + "Name": "Unnamed", + "ConstraintGrade": "NOTDEFINED", + "ObjectiveQualifier": "NOTDEFINED" + }) diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/assign_constraint.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/assign_constraint.py new file mode 100644 index 0000000000..6a8a8755ae --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/assign_constraint.py @@ -0,0 +1,28 @@ +import ifcopenshell + + +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "product": None, + "constraint": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + rel = self.get_constraint_rel() + related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set() + related_objects.add(self.settings["product"]) + rel.RelatedObjects = list(related_objects) + + def get_constraint_rel(self): + for rel in self.file.by_type("IfcRelAssociatesConstraint"): + if rel.RelatingConstraint == self.settings["constraint"]: + return rel + return self.file.create_entity("IfcRelAssociatesConstraint", **{ + "GlobalId": ifcopenshell.guid.new(), + # TODO: owner history + "RelatingConstraint": self.settings["constraint"] + }) diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/data.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/data.py new file mode 100644 index 0000000000..e756d9f2c8 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/data.py @@ -0,0 +1,43 @@ +import ifcopenshell +import ifcopenshell.util.date +from blenderbim.bim.ifc import IfcStore +from datetime import datetime + + +class Data: + is_loaded = False + products = {} + objectives = {} + + @classmethod + def load(cls, product_id=None): + cls._file = IfcStore.get_file() + if not cls._file: + return + if product_id: + return cls.load_product(product_id) + cls.load_objectives() + cls.is_loaded = True + + @classmethod + def load_product(cls, product_id): + product = cls._file.by_id(product_id) + cls.products[product_id] = [] + if not product.HasAssociations: + return + for association in product.HasAssociations: + if association.is_a("IfcRelAssociatesConstraint"): + if not association.RelatingConstraint.is_a("IfcObjective"): + continue # not yet implemented + cls.products[product_id].append(association.RelatingConstraint.id()) + + @classmethod + def load_objectives(cls): + cls.objectives = {} + for constraint in cls._file.by_type("IfcObjective"): + data = constraint.get_info() + if cls._file.schema == "IFC2X3": + for attribute in ["CreationTime"]: + if data[attribute]: + data[attribute] = ifcopenshell.util.date.ifc2datetime(data[attribute]).isoformat() + cls.objectives[constraint.id()] = data diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/edit_objective.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/edit_objective.py new file mode 100644 index 0000000000..9a1003ba70 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/edit_objective.py @@ -0,0 +1,13 @@ +class Usecase: + def __init__(self, file, settings=None): + self.file = file + self.settings = { + "objective": 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["objective"], name, value) diff --git a/src/ifcblenderexport/blenderbim/bim/module/constraint/operator.py b/src/ifcblenderexport/blenderbim/bim/module/constraint/operator.py new file mode 100644 index 0000000000..999f2dace7 --- /dev/null +++ b/src/ifcblenderexport/blenderbim/bim/module/constraint/operator.py @@ -0,0 +1,188 @@ +import bpy +import json +import blenderbim.bim.module.constraint.add_objective as add_objective +import blenderbim.bim.module.constraint.edit_objective as edit_objective +import blenderbim.bim.module.constraint.remove_constraint as remove_constraint +import blenderbim.bim.module.constraint.assign_constraint as assign_constraint +import blenderbim.bim.module.constraint.unassign_constraint as unassign_constraint +from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.constraint.data import Data + + +class LoadObjectives(bpy.types.Operator): + bl_idname = "bim.load_objectives" + bl_label = "Load Objectives" + + def execute(self, context): + props = context.scene.BIMConstraintProperties + while len(props.constraints) > 0: + props.constraints.remove(0) + for constraint_id, constraint in Data.objectives.items(): + new = props.constraints.add() + new.name = constraint["Name"] or "Unnamed" + new.ifc_definition_id = constraint_id + props.is_editing = "IfcObjective" + bpy.ops.bim.disable_editing_constraint() + return {"FINISHED"} + + +class DisableConstraintEditingUI(bpy.types.Operator): + bl_idname = "bim.disable_constraint_editing_ui" + bl_label = "Disable Constraint Editing UI" + + def execute(self, context): + context.scene.BIMConstraintProperties.is_editing = "" + bpy.ops.bim.disable_editing_constraint() + return {"FINISHED"} + + +class EnableEditingConstraint(bpy.types.Operator): + bl_idname = "bim.enable_editing_constraint" + bl_label = "Enable Editing Constraint" + constraint: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMConstraintProperties + while len(props.constraint_attributes) > 0: + props.constraint_attributes.remove(0) + + if props.is_editing == "IfcObjective": + data = Data.objectives[self.constraint] + + for attribute in IfcStore.get_schema().declaration_by_name(props.is_editing).all_attributes(): + data_type = str(attribute.type_of_attribute) + if "" in data_type: + new.string_value = "" if new.is_null else data[attribute.name()] + new.data_type = "string" + elif "= 0: - obj.BIMObjectProperties.constraints.remove(index) - return {"FINISHED"} - - -class RemoveObjectConstraint(bpy.types.Operator): - bl_idname = "bim.remove_object_constraint" - bl_label = "Remove Object Constraint" - index: bpy.props.IntProperty() - - def execute(self, context): - bpy.context.active_object.BIMObjectProperties.constraints.remove(self.index) - return {"FINISHED"} - - class AddMaterialAttribute(bpy.types.Operator): bl_idname = "bim.add_material_attribute" bl_label = "Add Material Attribute" diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index 36bad48d7f..af5812a98a 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -520,98 +520,6 @@ class SmartClashGroup(PropertyGroup): global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty) -class Constraint(PropertyGroup): - name: StringProperty(name="Name") - description: StringProperty(name="Description") - constraint_grade: EnumProperty( - items=[ - ( - "HARD", - "HARD", - "Qualifies a constraint such that it must be followed rigidly within or at the values set.", - ), - ("SOFT", "SOFT", "Qualifies a constraint such that it should be followed within or at the values set."), - ( - "ADVISORY", - "ADVISORY", - "Qualifies a constraint such that it is advised that it is followed within or at the values set.", - ), - ( - "USERDEFINED", - "USERDEFINED", - "A user-defined grade indicated by a separate attribute at the referencing entity.", - ), - ("NOTDEFINED", "NOTDEFINED", "Grade has not been specified."), - ], - name="Grade", - ) - constraint_source: StringProperty(name="Source") - user_defined_grade: StringProperty(name="Custom Grade") - objective_qualifier: EnumProperty( - items=[ - ( - "CODECOMPLIANCE", - "CODECOMPLIANCE", - "A constraint whose objective is to ensure satisfaction of a code compliance provision.", - ), - ( - "CODEWAIVER", - "CODEWAIVER", - "A constraint whose objective is to identify an agreement that code compliance requirements (the waiver) will not be enforced.", - ), - ( - "DESIGNINTENT", - "DESIGNINTENT", - "A constraint whose objective is to ensure satisfaction of a design intent provision.", - ), - ( - "EXTERNAL", - "EXTERNAL", - "A constraint whose objective is to synchronize data with an external source such as a file", - ), - ( - "HEALTHANDSAFETY", - "HEALTHANDSAFETY", - "A constraint whose objective is to ensure satisfaction of a health and safety provision.", - ), - ( - "MERGECONFLICT", - "MERGECONFLICT", - "A constraint whose objective is to resolve a conflict such as merging data from multiple sources.", - ), - ( - "MODELVIEW", - "MODELVIEW", - "A constraint whose objective is to ensure data conforms to a model view definition.", - ), - ( - "PARAMETER", - "PARAMETER", - "A constraint whose objective is to calculate a value based on other referenced values.", - ), - ( - "REQUIREMENT", - "REQUIREMENT", - "A constraint whose objective is to ensure satisfaction of a project requirement provision.", - ), - ( - "SPECIFICATION", - "SPECIFICATION", - "A constraint whose objective is to ensure satisfaction of a specification provision.", - ), - ( - "TRIGGERCONDITION", - "TRIGGERCONDITION", - "A constraint whose objective is to indicate a limiting value beyond which the condition of an object requires a particular form of attention.", - ), - ("USERDEFINED", "USERDEFINED", ""), - ("NOTDEFINED", "NOTDEFINED", ""), - ], - name="Qualifier", - ) - user_defined_qualifier: StringProperty(name="Custom Qualifier") - - class PropertySetTemplate(PropertyGroup): global_id: StringProperty(name="Global ID") name: StringProperty(name="Name") @@ -852,8 +760,6 @@ class BIMProperties(PropertyGroup): smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup) active_smart_group_index: IntProperty(name="Active Smart Group Index") smart_clash_grouping_max_distance: IntProperty(name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10) - constraints: CollectionProperty(name="Constraints", type=Constraint) - active_constraint_index: IntProperty(name="Active Constraint Index") ifc_patch_recipes: EnumProperty(items=getIfcPatchRecipes, name="Recipes") ifc_patch_input: StringProperty(default="", name="IFC Patch Input IFC") ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC") @@ -957,8 +863,6 @@ class BIMObjectProperties(PropertyGroup): relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object) psets: CollectionProperty(name="Psets", type=PsetQto) qtos: CollectionProperty(name="Qtos", type=PsetQto) - constraints: CollectionProperty(name="Constraints", type=Constraint) - active_constraint_index: IntProperty(name="Active Constraint Index") 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 046c938494..a169ca880d 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -37,96 +37,6 @@ class BIM_PT_object_structural(Panel): row.prop(props, "structural_member_connection") -class BIM_PT_constraints(Panel): - bl_label = "IFC Constraints" - bl_idname = "BIM_PT_constraints" - bl_options = {"DEFAULT_CLOSED"} - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "scene" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - props = context.scene.BIMProperties - - row = layout.row() - row.operator("bim.add_constraint") - - if props.constraints: - layout.template_list("BIM_UL_constraints", "", props, "constraints", props, "active_constraint_index") - - if props.active_constraint_index < len(props.constraints): - constraint = props.constraints[props.active_constraint_index] - row = layout.row(align=True) - row.prop(constraint, "name") - row.operator("bim.remove_constraint", icon="X", text="").index = props.active_constraint_index - row = layout.row() - row.prop(constraint, "description") - row = layout.row() - row.prop(constraint, "constraint_grade") - if constraint.constraint_grade == "USERDEFINED": - row = layout.row() - row.prop(constraint, "user_defined_grade") - row = layout.row() - row.prop(constraint, "constraint_source") - row = layout.row() - row.prop(constraint, "objective_qualifier") - if constraint.objective_qualifier == "USERDEFINED": - row = layout.row() - row.prop(constraint, "user_defined_qualifier") - - row = layout.row(align=True) - row.operator("bim.assign_constraint", text="Assign Constraint") - row.operator("bim.unassign_constraint", text="Unassign Constraint") - - -class BIM_PT_constraint_relations(Panel): - bl_label = "IFC Constraints" - bl_idname = "BIM_PT_constraint_relations" - bl_options = {"DEFAULT_CLOSED"} - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "object" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - props = context.active_object.BIMObjectProperties - - if not props.constraints: - layout.label(text="No constraints found") - - if props.constraints: - layout.template_list("BIM_UL_constraints", "", props, "constraints", props, "active_constraint_index") - - if props.active_constraint_index < len(props.constraints): - constraint = props.constraints[props.active_constraint_index] - row = layout.row(align=True) - row.prop(constraint, "name") - if constraint.name in bpy.context.scene.BIMProperties.constraints: - constraint = bpy.context.scene.BIMProperties.constraints[constraint.name] - row.operator( - "bim.remove_object_constraint", icon="X", text="" - ).index = props.active_constraint_index - row = layout.row() - row.prop(constraint, "description") - row = layout.row() - row.prop(constraint, "constraint_grade") - if constraint.constraint_grade == "USERDEFINED": - row = layout.row() - row.prop(constraint, "user_defined_grade") - row = layout.row() - row.prop(constraint, "constraint_source") - row = layout.row() - row.prop(constraint, "objective_qualifier") - if constraint.objective_qualifier == "USERDEFINED": - row = layout.row() - row.prop(constraint, "user_defined_qualifier") - else: - layout.label(text="Constraint is invalid") - - class BIM_PT_psets(Panel): bl_label = "IFC Property Sets" bl_idname = "BIM_PT_psets" @@ -719,15 +629,6 @@ class BIM_UL_smart_groups(bpy.types.UIList): layout.label(text="", translate=False) -class BIM_UL_constraints(bpy.types.UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - ob = data - if item: - layout.prop(item, "name", text="", emboss=False) - else: - layout.label(text="", translate=False) - - class BIM_ADDON_preferences(bpy.types.AddonPreferences): bl_idname = "blenderbim" svg2pdf_command: StringProperty(name="SVG to PDF Command", description="E.g. [['inkscape', svg, '-o', pdf]]")