mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
Add support for BCFXML export of IFC Clash results
This commit is contained in:
@@ -345,7 +345,7 @@ class BcfXml:
|
|||||||
|
|
||||||
def write_viewpoint(self, viewpoint, topic):
|
def write_viewpoint(self, viewpoint, topic):
|
||||||
document = minidom.Document()
|
document = minidom.Document()
|
||||||
root = self._create_element(document, "VisualizationInfo", { "Guid": viewpoint.guid })
|
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
|
||||||
self.write_viewpoint_components(viewpoint, root)
|
self.write_viewpoint_components(viewpoint, root)
|
||||||
self.write_viewpoint_orthogonal_camera(viewpoint, root)
|
self.write_viewpoint_orthogonal_camera(viewpoint, root)
|
||||||
self.write_viewpoint_perspective_camera(viewpoint, root)
|
self.write_viewpoint_perspective_camera(viewpoint, root)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import os
|
|||||||
import bpy
|
import bpy
|
||||||
import bcf
|
import bcf
|
||||||
from . import bcfstore
|
from . import bcfstore
|
||||||
|
from math import radians, degrees, atan, tan, cos, sin
|
||||||
|
from mathutils import Vector, Matrix, Euler, geometry
|
||||||
|
|
||||||
class NewBcfProject(bpy.types.Operator):
|
class NewBcfProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.new_bcf_project"
|
bl_idname = "bim.new_bcf_project"
|
||||||
|
|||||||
@@ -18,32 +18,37 @@ bcfviewpoints_enum = None
|
|||||||
|
|
||||||
|
|
||||||
def updateBcfReferenceLink(self, context):
|
def updateBcfReferenceLink(self, context):
|
||||||
bpy.ops.bim.edit_bcf_reference_links()
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
|
bpy.ops.bim.edit_bcf_reference_links()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfLabel(self, context):
|
def updateBcfLabel(self, context):
|
||||||
bpy.ops.bim.edit_bcf_labels()
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
|
bpy.ops.bim.edit_bcf_labels()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfProjectName(self, context):
|
def updateBcfProjectName(self, context):
|
||||||
bpy.ops.bim.edit_bcf_project_name()
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
|
bpy.ops.bim.edit_bcf_project_name()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfAuthor(self, context):
|
def updateBcfAuthor(self, context):
|
||||||
bpy.ops.bim.edit_bcf_author()
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
|
bpy.ops.bim.edit_bcf_author()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfTopicName(self, context):
|
def updateBcfTopicName(self, context):
|
||||||
bpy.ops.bim.edit_bcf_topic_name()
|
if bpy.context.scene.BCFProperties.is_loaded:
|
||||||
|
bpy.ops.bim.edit_bcf_topic_name()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfTopicIsEditable(self, context):
|
def updateBcfTopicIsEditable(self, context):
|
||||||
if not self.is_editable:
|
if bpy.context.scene.BCFProperties.is_loaded and not self.is_editable:
|
||||||
bpy.ops.bim.edit_bcf_topic()
|
bpy.ops.bim.edit_bcf_topic()
|
||||||
|
|
||||||
|
|
||||||
def updateBcfCommentIsEditable(self, context):
|
def updateBcfCommentIsEditable(self, context):
|
||||||
if not self.is_editable:
|
if bpy.context.scene.BCFProperties.is_loaded and not self.is_editable:
|
||||||
bpy.ops.bim.edit_bcf_comment(comment_guid = self.name)
|
bpy.ops.bim.edit_bcf_comment(comment_guid = self.name)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1568,11 +1568,12 @@ class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
|||||||
class ExecuteIfcClash(bpy.types.Operator):
|
class ExecuteIfcClash(bpy.types.Operator):
|
||||||
bl_idname = "bim.execute_ifc_clash"
|
bl_idname = "bim.execute_ifc_clash"
|
||||||
bl_label = "Execute IFC Clash"
|
bl_label = "Execute IFC Clash"
|
||||||
filename_ext = ".json"
|
filename_ext = ".bcf"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
if ".json" not in bpy.data.filepath:
|
||||||
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
|
||||||
WindowManager = context.window_manager
|
WindowManager = context.window_manager
|
||||||
WindowManager.fileselect_add(self)
|
WindowManager.fileselect_add(self)
|
||||||
return {"RUNNING_MODAL"}
|
return {"RUNNING_MODAL"}
|
||||||
@@ -1581,7 +1582,8 @@ class ExecuteIfcClash(bpy.types.Operator):
|
|||||||
import ifcclash
|
import ifcclash
|
||||||
|
|
||||||
settings = ifcclash.IfcClashSettings()
|
settings = ifcclash.IfcClashSettings()
|
||||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
if ".json" not in self.filepath:
|
||||||
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
|
||||||
settings.output = self.filepath
|
settings.output = self.filepath
|
||||||
settings.logger = logging.getLogger("Clash")
|
settings.logger = logging.getLogger("Clash")
|
||||||
settings.logger.setLevel(logging.DEBUG)
|
settings.logger.setLevel(logging.DEBUG)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import sys
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class Mesh:
|
class Mesh:
|
||||||
faces: []
|
faces: []
|
||||||
vertices: []
|
vertices: []
|
||||||
@@ -153,6 +154,64 @@ class IfcClasher:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def export(self):
|
def export(self):
|
||||||
|
if len(self.settings.output) > 4 and self.settings.output[-4:] == ".bcf":
|
||||||
|
return self.export_bcfxml()
|
||||||
|
self.export_json()
|
||||||
|
|
||||||
|
def export_bcfxml(self):
|
||||||
|
import bcf
|
||||||
|
import bcf.bcfxml
|
||||||
|
|
||||||
|
for i, clash_set in enumerate(self.clash_sets):
|
||||||
|
bcfxml = bcf.bcfxml.BcfXml()
|
||||||
|
bcfxml.new_project()
|
||||||
|
bcfxml.project.name = clash_set["name"]
|
||||||
|
bcfxml.edit_project()
|
||||||
|
for key, clash in clash_set["clashes"].items():
|
||||||
|
topic = bcf.data.Topic()
|
||||||
|
topic.title = "{}/{} and {}/{}".format(
|
||||||
|
clash["a_ifc_class"], clash["a_name"], clash["b_ifc_class"], clash["b_name"]
|
||||||
|
)
|
||||||
|
topic = bcfxml.add_topic(topic)
|
||||||
|
viewpoint = bcf.data.Viewpoint()
|
||||||
|
viewpoint.perspective_camera = bcf.data.PerspectiveCamera()
|
||||||
|
position = np.array(clash["position"])
|
||||||
|
point = position + np.array((5, 5, 5)) # Dumb, but works!
|
||||||
|
viewpoint.perspective_camera.camera_view_point.x = point[0]
|
||||||
|
viewpoint.perspective_camera.camera_view_point.y = point[1]
|
||||||
|
viewpoint.perspective_camera.camera_view_point.z = point[2]
|
||||||
|
mat = self.get_track_to_matrix(point, position)
|
||||||
|
viewpoint.perspective_camera.camera_direction.x = mat[0][2] * -1
|
||||||
|
viewpoint.perspective_camera.camera_direction.y = mat[1][2] * -1
|
||||||
|
viewpoint.perspective_camera.camera_direction.z = mat[2][2] * -1
|
||||||
|
viewpoint.perspective_camera.camera_up_vector.x = mat[0][1]
|
||||||
|
viewpoint.perspective_camera.camera_up_vector.y = mat[1][1]
|
||||||
|
viewpoint.perspective_camera.camera_up_vector.z = mat[2][1]
|
||||||
|
bcfxml.add_viewpoint(topic, viewpoint)
|
||||||
|
if i == 0:
|
||||||
|
bcfxml.save_project(self.settings.output)
|
||||||
|
else:
|
||||||
|
bcfxml.save_project(self.settings.output + f".{i}")
|
||||||
|
|
||||||
|
# https://blender.stackexchange.com/questions/68834/recreate-to-track-quat-with-two-vectors-using-python/141706#141706
|
||||||
|
def get_track_to_matrix(self, camera_position, target_position):
|
||||||
|
camera_direction = camera_position - target_position
|
||||||
|
camera_direction = camera_direction / np.linalg.norm(camera_direction)
|
||||||
|
camera_right = np.cross(np.array([0.0, 0.0, 1.0]), camera_direction)
|
||||||
|
camera_right = camera_right / np.linalg.norm(camera_right)
|
||||||
|
camera_up = np.cross(camera_direction, camera_right)
|
||||||
|
camera_up = camera_up / np.linalg.norm(camera_up)
|
||||||
|
rotation_transform = np.zeros((4, 4))
|
||||||
|
rotation_transform[0, :3] = camera_right
|
||||||
|
rotation_transform[1, :3] = camera_up
|
||||||
|
rotation_transform[2, :3] = camera_direction
|
||||||
|
rotation_transform[-1, -1] = 1
|
||||||
|
translation_transform = np.eye(4)
|
||||||
|
translation_transform[:3, -1] = - camera_position
|
||||||
|
look_at_transform = np.matmul(rotation_transform, translation_transform)
|
||||||
|
return np.linalg.inv(look_at_transform)
|
||||||
|
|
||||||
|
def export_json(self):
|
||||||
results = self.clash_sets.copy()
|
results = self.clash_sets.copy()
|
||||||
for result in results:
|
for result in results:
|
||||||
del result["a_cm"]
|
del result["a_cm"]
|
||||||
@@ -324,13 +383,13 @@ class IfcClasher:
|
|||||||
if not "clashes" in clash_set.keys():
|
if not "clashes" in clash_set.keys():
|
||||||
continue
|
continue
|
||||||
smart_groups = defaultdict(list)
|
smart_groups = defaultdict(list)
|
||||||
for clash_id, content in clash_set['clashes'].items():
|
for clash_id, content in clash_set["clashes"].items():
|
||||||
if "smart_group" in content:
|
if "smart_group" in content:
|
||||||
object_id_list = list()
|
object_id_list = list()
|
||||||
# Clash has been grouped, let's extract it.
|
# Clash has been grouped, let's extract it.
|
||||||
object_id_list.append(content['a_global_id'])
|
object_id_list.append(content["a_global_id"])
|
||||||
object_id_list.append(content['b_global_id'])
|
object_id_list.append(content["b_global_id"])
|
||||||
smart_groups[content['smart_group']].append(object_id_list)
|
smart_groups[content["smart_group"]].append(object_id_list)
|
||||||
count_of_smart_groups += len(smart_groups)
|
count_of_smart_groups += len(smart_groups)
|
||||||
output_clash_sets[clash_set["name"]].append(smart_groups)
|
output_clash_sets[clash_set["name"]].append(smart_groups)
|
||||||
|
|
||||||
@@ -346,8 +405,10 @@ class IfcClasher:
|
|||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
count_of_final_clash_sets = len(output_clash_sets)
|
count_of_final_clash_sets = len(output_clash_sets)
|
||||||
print(f"Took {count_of_input_clashes} clashes in {count_of_clash_sets} clash sets and turned",
|
print(
|
||||||
f"them into {count_of_smart_groups} smart groups in {count_of_final_clash_sets} clash sets")
|
f"Took {count_of_input_clashes} clashes in {count_of_clash_sets} clash sets and turned",
|
||||||
|
f"them into {count_of_smart_groups} smart groups in {count_of_final_clash_sets} clash sets",
|
||||||
|
)
|
||||||
|
|
||||||
return output_clash_sets
|
return output_clash_sets
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user