diff --git a/src/blenderbim/blenderbim/bim/module/clash/__init__.py b/src/blenderbim/blenderbim/bim/module/clash/__init__.py index 5cb662fa02..b87fb8c3ff 100644 --- a/src/blenderbim/blenderbim/bim/module/clash/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/clash/__init__.py @@ -25,15 +25,12 @@ classes = ( operator.ExecuteIfcClash, operator.ExportClashSets, operator.ImportClashSets, - operator.LoadIfcClashes, - operator.SaveIfcClashes, operator.LoadSmartGroupsForActiveClashSet, operator.RemoveClashSet, operator.RemoveClashSource, operator.SelectClash, operator.SelectClashResults, operator.SelectClashSource, - operator.SelectIfcClashResults, operator.SelectSmartGroup, operator.SelectSmartGroupedClashesPath, operator.SmartClashGroup, @@ -44,7 +41,6 @@ classes = ( prop.BIMClashProperties, ui.BIM_PT_ifcclash, ui.BIM_PT_clash_manager, - ui.BIM_PT_dumb_clash_manager, ui.BIM_PT_smart_clash_manager, ui.BIM_UL_clashes, ui.BIM_UL_clash_sets, diff --git a/src/blenderbim/blenderbim/bim/module/clash/data.py b/src/blenderbim/blenderbim/bim/module/clash/data.py new file mode 100644 index 0000000000..f6e788b82a --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/clash/data.py @@ -0,0 +1,154 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2024 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 ifcopenshell.util.date +import ifcopenshell.util.classification +import blenderbim.tool as tool +from blenderbim.bim.ifc import IfcStore + + +def refresh(): + ClassificationsData.is_loaded = False + ClassificationReferencesData.is_loaded = False + MaterialClassificationsData.is_loaded = False + CostClassificationsData.is_loaded = False + + +class ClassificationsData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data["has_classification_file"] = cls.has_classification_file() + cls.data["classifications"] = cls.classifications() + cls.data["available_classifications"] = cls.available_classifications() + + @classmethod + def has_classification_file(cls): + return bool(IfcStore.classification_file) + + @classmethod + def classifications(cls): + results = [] + for element in tool.Ifc.get().by_type("IfcClassification"): + data = element.get_info() + if tool.Ifc.get().schema == "IFC2X3" and element.EditionDate: + data["EditionDate"] = ifcopenshell.util.date.ifc2datetime(data["EditionDate"]) + results.append(data) + return results + + @classmethod + def available_classifications(cls): + if not IfcStore.classification_file: + return [] + return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")] + + +class ReferencesData: + @classmethod + def active_classification_library(cls): + if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"): + return False + props = bpy.context.scene.BIMClassificationProperties + name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name + if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]: + return name + + @classmethod + def classifications(cls): + return [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type("IfcClassification")] + + +class ClassificationReferencesData(ReferencesData): + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data["references"] = cls.references() + cls.data["active_classification_library"] = cls.active_classification_library() + cls.data["classifications"] = cls.classifications() + cls.data["object_type"] = "OBJECT" + + @classmethod + def references(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object) + if element: + for reference in ifcopenshell.util.classification.get_references(element): + data = reference.get_info() + del data["ReferencedSource"] + results.append(data) + return results + + +class MaterialClassificationsData(ReferencesData): + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data["references"] = cls.references() + cls.data["active_classification_library"] = cls.active_classification_library() + cls.data["classifications"] = cls.classifications() + cls.data["object_type"] = "MATERIAL" + + @classmethod + def references(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object.active_material) + if element: + for reference in ifcopenshell.util.classification.get_references(element): + data = reference.get_info() + del data["ReferencedSource"] + results.append(data) + return results + + +class CostClassificationsData(ReferencesData): + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.is_loaded = True + cls.data["references"] = cls.references() + cls.data["active_classification_library"] = cls.active_classification_library() + cls.data["classifications"] = cls.classifications() + cls.data["object_type"] = "COST" + + @classmethod + def references(cls): + results = [] + element = tool.Ifc.get().by_id( + bpy.context.scene.BIMCostProperties.cost_items[ + bpy.context.scene.BIMCostProperties.active_cost_item_index + ].ifc_definition_id + ) + if element: + for reference in ifcopenshell.util.classification.get_references(element): + data = reference.get_info() + del data["ReferencedSource"] + results.append(data) + return results diff --git a/src/blenderbim/blenderbim/bim/module/clash/decorator.py b/src/blenderbim/blenderbim/bim/module/clash/decorator.py new file mode 100644 index 0000000000..7afc8255e6 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/clash/decorator.py @@ -0,0 +1,76 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2024 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 gpu +import bmesh +import blenderbim.tool as tool +from bpy.types import SpaceView3D +from mathutils import Vector +from gpu_extras.batch import batch_for_shader + + +class ClashDecorator: + installed = None + + @classmethod + def install(cls, context): + if cls.installed: + cls.uninstall() + handler = cls() + cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW") + + @classmethod + def uninstall(cls): + try: + SpaceView3D.draw_handler_remove(cls.installed, "WINDOW") + except ValueError: + pass + cls.installed = None + + def draw_batch(self, shader_type, content_pos, color, indices=None): + shader = self.line_shader if shader_type == "LINES" else self.shader + batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices) + shader.uniform_float("color", color) + batch.draw(shader) + + def __call__(self, context): + self.addon_prefs = context.preferences.addons["blenderbim"].preferences + selected_elements_color = self.addon_prefs.decorator_color_selected + unselected_elements_color = self.addon_prefs.decorator_color_unselected + special_elements_color = self.addon_prefs.decorator_color_special + + gpu.state.point_size_set(6) + gpu.state.blend_set("ALPHA") + + self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") + self.line_shader.bind() # required to be able to change uniforms of the shader + # POLYLINE_UNIFORM_COLOR specific uniforms + self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height)) + self.line_shader.uniform_float("lineWidth", 2.0) + + # general shader + self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") + + selected_vertices = [context.scene.BIMClashProperties.p1, context.scene.BIMClashProperties.p2] + selected_edges = [] + if selected_vertices[0] != selected_vertices[1]: + selected_edges = [[0, 1]] + + self.draw_batch("POINTS", selected_vertices, special_elements_color) + if selected_edges: + self.draw_batch("LINES", selected_vertices, special_elements_color, selected_edges) diff --git a/src/blenderbim/blenderbim/bim/module/clash/operator.py b/src/blenderbim/blenderbim/bim/module/clash/operator.py index a56a45f560..4e4ad6fec2 100644 --- a/src/blenderbim/blenderbim/bim/module/clash/operator.py +++ b/src/blenderbim/blenderbim/bim/module/clash/operator.py @@ -23,10 +23,11 @@ import bmesh import logging import numpy as np import ifcopenshell -from mathutils import Matrix, Vector -from math import radians -from blenderbim.bim.ifc import IfcStore import blenderbim.tool as tool +from math import radians +from mathutils import Matrix, Vector +from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.clash.decorator import ClashDecorator class ExportClashSets(bpy.types.Operator): @@ -45,18 +46,7 @@ class ExportClashSets(bpy.types.Operator): def execute(self, context): self.filepath = bpy.path.ensure_ext(self.filepath, ".json") - clash_sets = [] - for clash_set in context.scene.BIMClashProperties.clash_sets: - self.a = [] - self.b = [] - for ab in ["a", "b"]: - for data in getattr(clash_set, ab): - clash_source = {"file": data.name} - if data.selector: - clash_source["selector"] = data.selector - clash_source["mode"] = data.mode - getattr(self, ab).append(clash_source) - clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b}) + clash_sets = tool.Clash.export_clash_sets() with open(self.filepath, "w") as destination: destination.write(json.dumps(clash_sets, indent=4)) return {"FINISHED"} @@ -78,25 +68,33 @@ class ImportClashSets(bpy.types.Operator): return {"RUNNING_MODAL"} def execute(self, context): - with open(self.filepath) as f: - clash_sets = json.load(f) - for clash_set in clash_sets: + tool.Clash.load_clash_sets(self.filepath) + for clash_set in tool.Clash.get_clash_sets(): new = context.scene.BIMClashProperties.clash_sets.add() new.name = clash_set["name"] - new.tolerance = clash_set["tolerance"] + new.mode = clash_set["mode"] + if new.mode == "intersection": + new.tolerance = clash_set["tolerance"] + new.check_all = clash_set["check_all"] + elif new.mode == "collision": + new.allow_touching = clash_set["allow_touching"] + elif new.mode == "clearance": + new.clearance = clash_set["clearance"] + new.check_all = clash_set["check_all"] for clash_source in clash_set["a"]: new_source = new.a.add() new_source.name = clash_source["file"] if "selector" in clash_source: new_source.selector = clash_source["selector"] new_source.mode = clash_source["mode"] - if clash_set["b"]: + if "b" in clash_set and clash_set["b"]: for clash_source in clash_set["b"]: new_source = new.b.add() new_source.name = clash_source["file"] if "selector" in clash_source: new_source.selector = clash_source["selector"] new_source.mode = clash_source["mode"] + tool.Clash.import_active_clashes() return {"FINISHED"} @@ -109,7 +107,6 @@ class AddClashSet(bpy.types.Operator): def execute(self, context): new = context.scene.BIMClashProperties.clash_sets.add() new.name = "New Clash Set" - new.tolerance = 0.01 return {"FINISHED"} @@ -220,6 +217,8 @@ class ExecuteIfcClash(bpy.types.Operator): def execute(self, context): from ifcclash import ifcclash + self.props = context.scene.BIMClashProperties + _, extension = os.path.splitext(self.filepath) if extension != ".json": self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf") @@ -230,7 +229,7 @@ class ExecuteIfcClash(bpy.types.Operator): settings.logger.setLevel(logging.DEBUG) clasher = ifcclash.Clasher(settings) - if context.scene.BIMClashProperties.should_create_clash_snapshots: + if self.props.should_create_clash_snapshots: def get_viewpoint_snapshot(viewpoint): camera = bpy.data.objects.get("IFC Clash Camera") @@ -271,23 +270,20 @@ class ExecuteIfcClash(bpy.types.Operator): clasher.get_viewpoint_snapshot = get_viewpoint_snapshot - clasher.clash_sets = [] - for clash_set in context.scene.BIMClashProperties.clash_sets: - self.a = [] - self.b = [] - for ab in ["a", "b"]: - for data in getattr(clash_set, ab): - clash_source = {"file": data.name} - if data.selector: - clash_source["selector"] = data.selector - clash_source["mode"] = data.mode - getattr(self, ab).append(clash_source) - clash_set_data = {"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a} - if self.b: - clash_set_data["b"] = self.b - clasher.clash_sets.append(clash_set_data) + clasher.clash_sets = tool.Clash.export_clash_sets() clasher.clash() clasher.export() + + if extension == ".json": + tool.Clash.load_clash_sets(self.filepath) + result = tool.Clash.get_clash_set(self.props.active_clash_set.name) + for clash in result["clashes"].values(): + blender_clash = self.props.active_clash_set.clashes.add() + blender_clash.a_global_id = clash["a_global_id"] + blender_clash.b_global_id = clash["b_global_id"] + blender_clash.a_name = "{}/{}".format(clash["a_ifc_class"], clash["a_name"]) + blender_clash.b_name = "{}/{}".format(clash["b_ifc_class"], clash["b_name"]) + blender_clash.status = False if not "status" in clash.keys() else clash["status"] return {"FINISHED"} @@ -306,6 +302,7 @@ class SelectIfcClashResults(bpy.types.Operator): return {"RUNNING_MODAL"} def execute(self, context): + # TODO refactor into new clash results system self.file = IfcStore.get_file() self.filepath = bpy.path.ensure_ext(self.filepath, ".json") with open(self.filepath) as f: @@ -352,83 +349,6 @@ class SelectIfcClashResults(bpy.types.Operator): return {"FINISHED"} -class LoadIfcClashes(bpy.types.Operator): - bl_idname = "bim.load_ifc_clashes" - bl_label = "Load IFC Clashes" - bl_options = {"REGISTER", "UNDO"} - bl_description = "Load the clashing IFC geometry stored in a file" - filename_ext = ".json" - filepath: bpy.props.StringProperty(subtype="FILE_PATH") - - def invoke(self, context, event): - self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json") - WindowManager = context.window_manager - WindowManager.fileselect_add(self) - return {"RUNNING_MODAL"} - - def execute(self, context): - self.file = IfcStore.get_file() - self.filepath = bpy.path.ensure_ext(self.filepath, ".json") - with open(self.filepath) as f: - clash_sets_json = json.load(f) - active_clash_set = context.scene.BIMClashProperties.active_clash_set - - for clash_set in clash_sets_json: - if not "clashes" in clash_set.keys(): - self.report({"WARNING"}, "No clashes found for the selected Clash Set.") - return {"CANCELLED"} - active_clash_set.clashes.clear() - for clash in clash_set["clashes"].values(): - blender_clash = active_clash_set.clashes.add() - blender_clash.a_global_id = clash["a_global_id"] - blender_clash.b_global_id = clash["b_global_id"] - blender_clash.a_name = "{}/{}".format(clash["a_ifc_class"], clash["a_name"]) - blender_clash.b_name = "{}/{}".format(clash["b_ifc_class"], clash["b_name"]) - blender_clash.status = False if not "status" in clash.keys() else clash["status"] - return {"FINISHED"} - - -class SaveIfcClashes(bpy.types.Operator): - bl_idname = "bim.save_ifc_clashes" - bl_label = "Save IFC Clashes" - bl_options = {"REGISTER", "UNDO"} - bl_description = "Save the clashing IFC geometry stored in a file" - filename_ext = ".json" - filepath: bpy.props.StringProperty(subtype="FILE_PATH") - - def invoke(self, context, event): - self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json") - WindowManager = context.window_manager - WindowManager.fileselect_add(self) - return {"RUNNING_MODAL"} - - def execute(self, context): - self.file = IfcStore.get_file() - self.filepath = bpy.path.ensure_ext(self.filepath, ".json") - clash_sets_json = self.load_json() - active_clash_set = context.scene.BIMClashProperties.active_clash_set - self.update_clash_sets(clash_sets_json, active_clash_set) - self.save_json(clash_sets_json) - return {"FINISHED"} - - def load_json(self): - with open(self.filepath) as f: - return json.load(f) - - def update_clash_sets(self, clash_sets_json, active_clash_set): - for clash_set in clash_sets_json: - if clash_set["name"] != active_clash_set.name or "clashes" not in clash_set.keys(): - continue - for clash in active_clash_set.clashes: - clash_key = clash.a_global_id + "-" + clash.b_global_id - if clash_set.get("clashes", {}).get(clash_key, None) is not None: - clash_set["clashes"][clash_key]["status"] = clash.status - - def save_json(self, clash_sets_json): - with open(self.filepath, "w") as destination: - json.dump(clash_sets_json, destination, indent=4) - - class SelectClash(bpy.types.Operator): bl_idname = "bim.select_clash" bl_label = "Select Clash" @@ -437,31 +357,28 @@ class SelectClash(bpy.types.Operator): index: bpy.props.IntProperty() def execute(self, context): - clash = context.scene.BIMClashProperties.active_clash + self.props = context.scene.BIMClashProperties + clash_set = tool.Clash.get_clash_set(self.props.active_clash_set.name) + active_clash = self.props.active_clash + clash = tool.Clash.get_clash(clash_set, active_clash.a_global_id, active_clash.b_global_id) + + if not clash: + return {"FINISHED"} products = [] - linked_objects = [] - try: - products.append(tool.Ifc.get().by_guid(clash.a_global_id)) - except: - linked_objects.append(clash.a_name) - try: - products.append(tool.Ifc.get().by_guid(clash.b_global_id)) - except: - linked_objects.append(clash.b_name) + + for global_id in (clash["a_global_id"], clash["b_global_id"]): + try: + products.append(tool.Ifc.get().by_guid(global_id)) + except: + pass tool.Spatial.select_products(products, unhide=True) - print(linked_objects) - for obj_name in linked_objects: - obj = bpy.data.objects.get(obj_name) - if obj: - obj.select_set(True) - else: - print("Object not found:", obj_name) - if context.scene.BIMClashProperties.sould_focus_on_clash: - context_override = tool.Blender.get_viewport_context() - with bpy.context.temp_override(**context_override): - bpy.ops.view3d.view_selected() + ClashDecorator.install(bpy.context) + target = Vector(clash["p1"]) + tool.Clash.look_at(target, target + Vector((5, 5, 5))) + self.props.p1 = clash["p1"] + self.props.p2 = clash["p2"] return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/clash/prop.py b/src/blenderbim/blenderbim/bim/module/clash/prop.py index 6bd8120fed..39c839118b 100644 --- a/src/blenderbim/blenderbim/bim/module/clash/prop.py +++ b/src/blenderbim/blenderbim/bim/module/clash/prop.py @@ -53,7 +53,24 @@ class Clash(PropertyGroup): class ClashSet(PropertyGroup): name: StringProperty(name="Name") - tolerance: FloatProperty(name="Tolerance") + mode: EnumProperty( + items=[ + ( + "intersection", + "Intersection", + "Detect objects that protrude or pierce another object", + "PIVOT_MEDIAN", + 1, + ), + ("collision", "Collision", "Detect touching objects with any surface collision", "PIVOT_INDIVIDUAL", 2), + ("clearance", "Clearance", "Detect objects within a proximity threshold", "PIVOT_ACTIVE", 3), + ], + name="Mode", + ) + tolerance: FloatProperty(name="Tolerance", default=0.002) + clearance: FloatProperty(name="Clearance", default=0.01) + allow_touching: BoolProperty(name="Allow Touching", default=False) + check_all: BoolProperty(name="Check All", default=False) a: CollectionProperty(name="Group A", type=ClashSource) b: CollectionProperty(name="Group B", type=ClashSource) clashes: CollectionProperty(name="Clashes", type=Clash) @@ -78,22 +95,20 @@ class BIMClashProperties(PropertyGroup): smart_clash_grouping_max_distance: IntProperty( name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10 ) - sould_focus_on_clash: BoolProperty(name="Show Focus Clash", default=False) + p1: FloatVectorProperty(name="P1", default=(0.0, 0.0, 0.0), subtype="XYZ") + p2: FloatVectorProperty(name="P2", default=(0.0, 0.0, 0.0), subtype="XYZ") @property def active_clash_set(self): - if not self.clash_sets: - return None - return self.clash_sets[self.active_clash_set_index] + if self.active_clash_set_index < len(self.clash_sets): + return self.clash_sets[self.active_clash_set_index] @property def active_smart_group(self): - if not self.smart_clash_groups: - return None - return self.smart_clash_groups[self.active_smart_group_index] + if self.active_smart_group_index < len(self.smart_clash_groups): + return self.smart_clash_groups[self.active_smart_group_index] @property def active_clash(self): - if not self.active_clash_set: - return None - return self.active_clash_set.clashes[self.active_clash_index] + if self.active_clash_index < len(self.active_clash_set.clashes): + return self.active_clash_set.clashes[self.active_clash_index] diff --git a/src/blenderbim/blenderbim/bim/module/clash/ui.py b/src/blenderbim/blenderbim/bim/module/clash/ui.py index 8b7ba0110f..204ece028d 100644 --- a/src/blenderbim/blenderbim/bim/module/clash/ui.py +++ b/src/blenderbim/blenderbim/bim/module/clash/ui.py @@ -45,57 +45,79 @@ class BIM_PT_ifcclash(Panel): layout.template_list("BIM_UL_clash_sets", "", props, "clash_sets", props, "active_clash_set_index") - if props.active_clash_set_index < len(props.clash_sets): - clash_set = props.active_clash_set + if not props.active_clash_set: + return - row = layout.row(align=True) - row.prop(clash_set, "name") - row.operator("bim.remove_clash_set", icon="X", text="").index = props.active_clash_set_index + clash_set = props.active_clash_set - row = layout.row(align=True) + row = layout.row(align=True) + row.prop(clash_set, "name") + row.operator("bim.remove_clash_set", icon="X", text="").index = props.active_clash_set_index + + row = layout.row() + row.prop(clash_set, "mode") + + if clash_set.mode == "intersection": + row = layout.row() row.prop(clash_set, "tolerance") - - layout.label(text="Group A:") row = layout.row() - row.operator("bim.add_clash_source").group = "a" - - for index, source in enumerate(clash_set.a): - row = layout.row(align=True) - row.prop(source, "name", text="") - op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") - op.index = index - op.group = "a" - op = row.operator("bim.remove_clash_source", icon="X", text="") - op.index = index - op.group = "a" - - row = layout.row(align=True) - row.prop(source, "mode", text="") - row.prop(source, "selector", text="") - - layout.label(text="Group B:") + row.prop(clash_set, "check_all") + elif clash_set.mode == "collision": row = layout.row() - row.operator("bim.add_clash_source").group = "b" - - for index, source in enumerate(clash_set.b): - row = layout.row(align=True) - row.prop(source, "name", text="") - op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") - op.index = index - op.group = "b" - op = row.operator("bim.remove_clash_source", icon="X", text="") - op.index = index - op.group = "b" - - row = layout.row(align=True) - row.prop(source, "mode", text="") - row.prop(source, "selector", text="") - + row.prop(clash_set, "allow_touching") + elif clash_set.mode == "clearance": row = layout.row() - row.prop(props, "should_create_clash_snapshots") + row.prop(clash_set, "clearance") + row = layout.row() + row.prop(clash_set, "check_all") + + row = layout.row(align=True) + row.label(text="Group A:", icon="OUTLINER_OB_POINTCLOUD") + row.operator("bim.add_clash_source", icon="ADD", text="").group = "a" + + for index, source in enumerate(clash_set.a): row = layout.row(align=True) - row.operator("bim.execute_ifc_clash") - row.operator("bim.select_ifc_clash_results") + row.prop(source, "name", text="") + op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") + op.index = index + op.group = "a" + op = row.operator("bim.remove_clash_source", icon="X", text="") + op.index = index + op.group = "a" + + row = layout.row(align=True) + row.prop(source, "mode", text="") + row.prop(source, "selector", text="") + + row = layout.row(align=True) + row.label(text="Group B:", icon="OUTLINER_OB_POINTCLOUD") + row.operator("bim.add_clash_source", icon="ADD", text="").group = "b" + + for index, source in enumerate(clash_set.b): + row = layout.row(align=True) + row.prop(source, "name", text="") + op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") + op.index = index + op.group = "b" + op = row.operator("bim.remove_clash_source", icon="X", text="") + op.index = index + op.group = "b" + + row = layout.row(align=True) + row.prop(source, "mode", text="") + row.prop(source, "selector", text="") + + row = layout.row() + row.prop(props, "should_create_clash_snapshots") + row = layout.row() + row.operator("bim.execute_ifc_clash") + + row = layout.row() + row.label(text=f"{len(clash_set.clashes)} Clashes Found", icon="PIVOT_CURSOR") + + layout.template_list("BIM_UL_clashes", "", props.active_clash_set, "clashes", props, "active_clash_index") + row = layout.row() + row.operator("bim.select_clash") class BIM_PT_clash_manager(Panel): @@ -111,29 +133,6 @@ class BIM_PT_clash_manager(Panel): pass -class BIM_PT_dumb_clash_manager(Panel): - bl_idname = "BIM_PT_dumb_clash_manager" - bl_label = "Dumb Manager" - bl_options = {"DEFAULT_CLOSED"} - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "scene" - bl_parent_id = "BIM_PT_clash_manager" - - def draw(self, context): - layout = self.layout - props = context.scene.BIMClashProperties - - row = layout.row(align=True) - row.operator("bim.load_ifc_clashes", text="LOAD IFC CLASHES", icon="IMPORT") - row.operator("bim.save_ifc_clashes", text="SAVE CLASH RESULTS", icon="EXPORT") - layout.template_list("BIM_UL_clashes", "", props.active_clash_set, "clashes", props, "active_clash_index") - # bim.select_clash - row = layout.row(align=True) - row.operator("bim.select_clash", text="SELECT CLASH", icon="IMPORT") - row.prop(props, "sould_focus_on_clash", icon="RESTRICT_VIEW_OFF") - - class BIM_PT_smart_clash_manager(Panel): bl_idname = "BIM_PT_smart_clash_manager" bl_label = "Smart Clash Manager"