Fix #3355. Reimplement graphical clash snapshots in BCF from IfcClash in the BlenderBIM Add-on.

This commit is contained in:
Dion Moult
2023-07-03 16:49:04 +10:00
parent 6758d878d8
commit 99e2598c00
3 changed files with 33 additions and 7 deletions
+2
View File
@@ -275,6 +275,7 @@ class TopicHandler:
""" """
new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler) new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler)
self.add_visinfo_handler(new_viewpoint) self.add_visinfo_handler(new_viewpoint)
return new_viewpoint
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None: 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 """Add a viewpoint pointing at an XYZ point in space
@@ -287,6 +288,7 @@ class TopicHandler:
position, *guids, xml_handler=self._xml_handler position, *guids, xml_handler=self._xml_handler
) )
self.add_visinfo_handler(vi_handler) self.add_visinfo_handler(vi_handler)
return vi_handler
def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None: def add_visinfo_handler(self, new_viewpoint: VisualizationInfoHandler) -> None:
self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint self.viewpoints[new_viewpoint.guid + ".bcfv"] = new_viewpoint
@@ -23,7 +23,7 @@ import bmesh
import logging import logging
import numpy as np import numpy as np
import ifcopenshell import ifcopenshell
from mathutils import Matrix 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
@@ -222,6 +222,7 @@ class ExecuteIfcClash(bpy.types.Operator):
_, extension = os.path.splitext(self.filepath) _, extension = os.path.splitext(self.filepath)
if extension != ".json": if extension != ".json":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf") self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
settings = ifcclash.ClashSettings() settings = ifcclash.ClashSettings()
settings.output = self.filepath settings.output = self.filepath
settings.logger = logging.getLogger("Clash") settings.logger = logging.getLogger("Clash")
@@ -230,12 +231,28 @@ class ExecuteIfcClash(bpy.types.Operator):
if context.scene.BIMClashProperties.should_create_clash_snapshots: 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") camera = bpy.data.objects.get("IFC Clash Camera")
if not camera: if not camera:
camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera")) camera = bpy.data.objects.new("IFC Clash Camera", bpy.data.cameras.new("IFC Clash Camera"))
context.scene.collection.objects.link(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 context.scene.camera = camera
camera.data.angle = radians(60) camera.data.angle = radians(60)
area = next(area for area in context.screen.areas if area.type == "VIEW_3D") 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.image_settings.file_format = "PNG"
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png") context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True) 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 clasher.get_viewpoint_snapshot = get_viewpoint_snapshot
+9 -3
View File
@@ -122,14 +122,20 @@ class Clasher:
for clash in clash_set["clashes"].values(): for clash in clash_set["clashes"].values():
title = f'{clash["a_ifc_class"]}/{clash["a_name"]} and {clash["b_ifc_class"]}/{clash["b_name"]}' 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 = 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"], 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 "" suffix = f".{i}" if i else ""
bcfxml.save_project(f"{self.settings.output}{suffix}") bcfxml.save_project(f"{self.settings.output}{suffix}")
def get_viewpoint_snapshot(self, viewpoint, mat): def get_viewpoint_snapshot(self, viewpoint):
return None # Possible to overload this function in a GUI application if used as a library # 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): def export_json(self):
clash_sets = self.clash_sets.copy() clash_sets = self.clash_sets.copy()