From 99e2598c00da10e8a887774e6095fa09adeae156 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 3 Jul 2023 16:49:04 +1000 Subject: [PATCH] Fix #3355. Reimplement graphical clash snapshots in BCF from IfcClash in the BlenderBIM Add-on. --- src/bcf/src/bcf/v2/topic.py | 2 ++ .../blenderbim/bim/module/clash/operator.py | 26 ++++++++++++++++--- src/ifcclash/ifcclash/ifcclash.py | 12 ++++++--- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/bcf/src/bcf/v2/topic.py b/src/bcf/src/bcf/v2/topic.py index 87cea5744a..c1188c9a06 100644 --- a/src/bcf/src/bcf/v2/topic.py +++ b/src/bcf/src/bcf/v2/topic.py @@ -275,6 +275,7 @@ class TopicHandler: """ new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler) self.add_visinfo_handler(new_viewpoint) + return new_viewpoint def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None: """Add a viewpoint pointing at an XYZ point in space @@ -287,6 +288,7 @@ class TopicHandler: position, *guids, xml_handler=self._xml_handler ) self.add_visinfo_handler(vi_handler) + return vi_handler def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None: self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint diff --git a/src/blenderbim/blenderbim/bim/module/clash/operator.py b/src/blenderbim/blenderbim/bim/module/clash/operator.py index f224d40859..933cc06b48 100644 --- a/src/blenderbim/blenderbim/bim/module/clash/operator.py +++ b/src/blenderbim/blenderbim/bim/module/clash/operator.py @@ -23,7 +23,7 @@ import bmesh import logging import numpy as np import ifcopenshell -from mathutils import Matrix +from mathutils import Matrix, Vector from math import radians from blenderbim.bim.ifc import IfcStore @@ -222,6 +222,7 @@ class ExecuteIfcClash(bpy.types.Operator): _, extension = os.path.splitext(self.filepath) if extension != ".json": self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf") + settings = ifcclash.ClashSettings() settings.output = self.filepath settings.logger = logging.getLogger("Clash") @@ -230,12 +231,28 @@ class ExecuteIfcClash(bpy.types.Operator): if context.scene.BIMClashProperties.should_create_clash_snapshots: - def get_viewpoint_snapshot(viewpoint, mat): + def get_viewpoint_snapshot(viewpoint): camera = bpy.data.objects.get("IFC Clash Camera") if not camera: camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera")) context.scene.collection.objects.link(camera) - camera.matrix_world = Matrix(mat) + + bcf_camera = viewpoint.visualization_info.perspective_camera + p = bcf_camera.camera_view_point + z = bcf_camera.camera_direction + z = Vector([z.x, z.y, z.z]) * -1 + y = bcf_camera.camera_up_vector + y = Vector([y.x, y.y, y.z]) + x = y.cross(z) + + mat = Matrix([ + [x[0], y[0], z[0], p.x], + [x[1], y[1], z[1], p.y], + [x[2], y[2], z[2], p.z], + [0, 0, 0, 0], + ]) + + camera.matrix_world = mat context.scene.camera = camera camera.data.angle = radians(60) area = next(area for area in context.screen.areas if area.type == "VIEW_3D") @@ -246,7 +263,8 @@ class ExecuteIfcClash(bpy.types.Operator): context.scene.render.image_settings.file_format = "PNG" context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png") bpy.ops.render.opengl(write_still=True) - return context.scene.render.filepath + with open(context.scene.render.filepath, "rb") as f: + return ("snapshot.png", f.read()) clasher.get_viewpoint_snapshot = get_viewpoint_snapshot diff --git a/src/ifcclash/ifcclash/ifcclash.py b/src/ifcclash/ifcclash/ifcclash.py index 1513e89c36..1406f40aee 100644 --- a/src/ifcclash/ifcclash/ifcclash.py +++ b/src/ifcclash/ifcclash/ifcclash.py @@ -122,14 +122,20 @@ class Clasher: for clash in clash_set["clashes"].values(): title = f'{clash["a_ifc_class"]}/{clash["a_name"]} and {clash["b_ifc_class"]}/{clash["b_name"]}' topic = bcfxml.add_topic(title, title, "IfcClash") - topic.add_viewpoint_from_point_and_guids( + viewpoint = topic.add_viewpoint_from_point_and_guids( np.array(clash["position"]), clash["a_global_id"], clash["b_global_id"], ) + snapshot = self.get_viewpoint_snapshot(viewpoint) + if snapshot: + topic.markup.viewpoints[0].snapshot = snapshot[0] + viewpoint.snapshot = snapshot[1] suffix = f".{i}" if i else "" bcfxml.save_project(f"{self.settings.output}{suffix}") - def get_viewpoint_snapshot(self, viewpoint, mat): - return None # Possible to overload this function in a GUI application if used as a library + def get_viewpoint_snapshot(self, viewpoint): + # Possible to overload this function in a GUI application if used as a library. + # Should return a tuple of (filename, bytes). + return None def export_json(self): clash_sets = self.clash_sets.copy()