mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
New Clash management panel to load IFCClash collision results
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user