New Clash management panel to load IFCClash collision results

This commit is contained in:
Sigma Dimensions (Yass)
2023-12-23 09:14:08 +01:00
parent e87e53f840
commit 6660ee3788
4 changed files with 202 additions and 15 deletions
@@ -20,26 +20,33 @@ import bpy
from . import ui, prop, operator from . import ui, prop, operator
classes = ( classes = (
operator.AddClashSet,
operator.AddClashSource,
operator.ExecuteIfcClash,
operator.ExportClashSets, operator.ExportClashSets,
operator.ImportClashSets, operator.ImportClashSets,
operator.AddClashSet, operator.LoadIfcClashes,
operator.SaveIfcClashes,
operator.LoadSmartGroupsForActiveClashSet,
operator.RemoveClashSet, operator.RemoveClashSet,
operator.AddClashSource,
operator.RemoveClashSource, operator.RemoveClashSource,
operator.SelectClashSource, operator.SelectClash,
operator.ExecuteIfcClash,
operator.SelectIfcClashResults,
operator.SelectClashResults, operator.SelectClashResults,
operator.SelectClashSource,
operator.SelectIfcClashResults,
operator.SelectSmartGroup,
operator.SelectSmartGroupedClashesPath, operator.SelectSmartGroupedClashesPath,
operator.SmartClashGroup, operator.SmartClashGroup,
operator.SelectSmartGroup, prop.Clash,
operator.LoadSmartGroupsForActiveClashSet,
prop.ClashSource, prop.ClashSource,
prop.ClashSet, prop.ClashSet,
prop.SmartClashGroup, prop.SmartClashGroup,
prop.BIMClashProperties, prop.BIMClashProperties,
ui.BIM_PT_ifcclash, ui.BIM_PT_ifcclash,
ui.BIM_PT_clash_manager, 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, ui.BIM_UL_clash_sets,
ui.BIM_UL_smart_groups, ui.BIM_UL_smart_groups,
) )
@@ -26,6 +26,7 @@ import ifcopenshell
from mathutils import Matrix, Vector from mathutils import Matrix, Vector
from math import radians from math import radians
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
class ExportClashSets(bpy.types.Operator): class ExportClashSets(bpy.types.Operator):
@@ -245,12 +246,14 @@ class ExecuteIfcClash(bpy.types.Operator):
y = Vector([y.x, y.y, y.z]) y = Vector([y.x, y.y, y.z])
x = y.cross(z) x = y.cross(z)
mat = Matrix([ mat = Matrix(
[x[0], y[0], z[0], p.x], [
[x[1], y[1], z[1], p.y], [x[0], y[0], z[0], p.x],
[x[2], y[2], z[2], p.z], [x[1], y[1], z[1], p.y],
[0, 0, 0, 0], [x[2], y[2], z[2], p.z],
]) [0, 0, 0, 0],
]
)
camera.matrix_world = mat camera.matrix_world = mat
context.scene.camera = camera context.scene.camera = camera
@@ -349,6 +352,119 @@ class SelectIfcClashResults(bpy.types.Operator):
return {"FINISHED"} 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"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select the clashing IFC geometry stored in a file"
index: bpy.props.IntProperty()
def execute(self, context):
clash = context.scene.BIMClashProperties.active_clash
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)
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()
return {"FINISHED"}
class SmartClashGroup(bpy.types.Operator): class SmartClashGroup(bpy.types.Operator):
bl_idname = "bim.smart_clash_group" bl_idname = "bim.smart_clash_group"
bl_label = "Smart Group Clashes" bl_label = "Smart Group Clashes"
@@ -433,9 +549,9 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
new_group = context.scene.BIMClashProperties.smart_clash_groups.add() new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
new_group.number = f"{smart_group}" new_group.number = f"{smart_group}"
for pair in global_id_pairs: for pair in global_id_pairs:
for id in pair: for guid in pair:
new_global_id = new_group.global_ids.add() new_global_id = new_group.global_ids.add()
new_global_id.name = id new_global_id.guid = guid
return {"FINISHED"} return {"FINISHED"}
@@ -43,11 +43,20 @@ class ClashSource(PropertyGroup):
) )
class Clash(PropertyGroup):
a_global_id: StringProperty(name="A")
b_global_id: StringProperty(name="B")
a_name: StringProperty(name="A Name")
b_name: StringProperty(name="B Name")
status: BoolProperty(name="Status", default=False)
class ClashSet(PropertyGroup): class ClashSet(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
tolerance: FloatProperty(name="Tolerance") tolerance: FloatProperty(name="Tolerance")
a: CollectionProperty(name="Group A", type=ClashSource) a: CollectionProperty(name="Group A", type=ClashSource)
b: CollectionProperty(name="Group B", type=ClashSource) b: CollectionProperty(name="Group B", type=ClashSource)
clashes: CollectionProperty(name="Clashes", type=Clash)
class SmartClashGroup(PropertyGroup): class SmartClashGroup(PropertyGroup):
@@ -63,11 +72,13 @@ class BIMClashProperties(PropertyGroup):
clash_results_path: StringProperty(name="Clash Results Path") clash_results_path: StringProperty(name="Clash Results Path")
smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path") smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path")
active_clash_set_index: IntProperty(name="Active Clash Set Index") active_clash_set_index: IntProperty(name="Active Clash Set Index")
active_clash_index: IntProperty(name="Active Clash Index")
smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup) smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup)
active_smart_group_index: IntProperty(name="Active Smart Group Index") active_smart_group_index: IntProperty(name="Active Smart Group Index")
smart_clash_grouping_max_distance: IntProperty( smart_clash_grouping_max_distance: IntProperty(
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10 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)
@property @property
def active_clash_set(self): def active_clash_set(self):
@@ -80,3 +91,9 @@ class BIMClashProperties(PropertyGroup):
if not self.smart_clash_groups: if not self.smart_clash_groups:
return None return None
return self.smart_clash_groups[self.active_smart_group_index] 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]
@@ -107,6 +107,42 @@ class BIM_PT_clash_manager(Panel):
bl_context = "scene" bl_context = "scene"
bl_parent_id = "BIM_PT_tab_clash_detection" bl_parent_id = "BIM_PT_tab_clash_detection"
def draw(self, context):
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"
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): def draw(self, context):
layout = self.layout layout = self.layout
props = context.scene.BIMClashProperties props = context.scene.BIMClashProperties
@@ -156,3 +192,14 @@ class BIM_UL_smart_groups(bpy.types.UIList):
layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0) layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0)
else: else:
layout.label(text="", translate=False) layout.label(text="", translate=False)
class BIM_UL_clashes(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=str(item.a_name), translate=False, icon="NONE", icon_value=0)
row.label(text=str(item.b_name), translate=False, icon="NONE", icon_value=0)
row.prop(item, "status", text="")
else:
layout.label(text="", translate=False)