IfcTester can now output reports in BCF

This commit is contained in:
Dion Moult
2023-02-20 17:50:14 +11:00
parent 3c17718f70
commit dde847cb5a
2 changed files with 32 additions and 95 deletions
+2
View File
@@ -57,6 +57,8 @@ elif args.reporter == "Html":
engine = reporter.Html(specs)
elif args.reporter == "Ods":
engine = reporter.Ods(specs)
elif args.reporter == "Bcf":
engine = reporter.Bcf(specs)
engine.report()
+30 -95
View File
@@ -169,10 +169,7 @@ class Json(Reporter):
{
"description": requirement.to_string("requirement"),
"status": requirement.status,
"failed_entities": [
{"reason": requirement.failed_reasons[i], "element": str(e)}
for i, e in enumerate(requirement.failed_entities[0:10])
],
"failed_entities": self.report_failed_entities(requirement),
}
)
total = len(specification.applicable_entities)
@@ -188,6 +185,12 @@ class Json(Reporter):
"requirements": requirements,
}
def report_failed_entities(self, requirement):
return [
{"reason": requirement.failed_reasons[i], "element": str(e)}
for i, e in enumerate(requirement.failed_entities)
]
def to_string(self):
import json
@@ -321,97 +324,29 @@ class Ods(Json):
self.doc.save(filepath, True)
class Bcf(Reporter):
def __init__(self, ids, with_viewpoint=True, filepath=None, group=False):
super().__init__(ids)
class Bcf(Json):
def report_failed_entities(self, requirement):
return [
{"reason": requirement.failed_reasons[i], "element": e} for i, e in enumerate(requirement.failed_entities)
]
def to_file(self, filepath):
from bcf.v2.bcfxml import BcfXml
self.with_viewpoint = with_viewpoint
self.group = group
self.bcf = BcfXml()
self.bcf.new_project()
if not filepath:
self.filepath=os.path.join(sys.path[0], self.ids.info['title'] + '.bcf')
# self.bcf.filepath = sys.path[0]
self.bcf.project.name = self.ids.info.get("title", "Untitled IDS")
self.bcf.author = self.ids.info.get("author", "your@email.com")
self.bcf.creation_date = datetime.datetime.today().replace(microsecond=0).isoformat()
self.bcf.modified_date = datetime.datetime.today().replace(microsecond=0).isoformat()
def report(self):
for specification in self.ids.specifications:
self.report_specification(specification, save_project=False)
self.bcf.save_project(self.filepath)
def report_specification(self, specification, save_project=True):
from bcf.v2 import data as bcf
for requirement in specification.requirements:
if self.group:
topic = bcf.Topic()
topic.title = requirement.to_string("requirement")
topic.description = ";\n".join([requirement.failed_reasons[i] + " for " + str(e.get_info()['type']) + ": " + str(e.GlobalId) for i, e in enumerate(requirement.failed_entities)])
self.bcf.add_topic(topic)
if self.with_viewpoint:
self.add_viewpoint(topic, requirement)
else:
for i, e in enumerate(requirement.failed_entities):
topic = bcf.Topic()
topic.title = requirement.to_string("requirement")
topic.description = requirement.failed_reasons[i] + " for " + str(e.get_info()['type']) + ": " + str(e.GlobalId)
self.bcf.add_topic(topic)
if self.with_viewpoint:
self.add_viewpoint(topic, requirement)
if save_project:
self.bcf.save_project(self.filepath)
def add_viewpoint(self, topic, requirement):
import numpy as np
import ifcopenshell.util.placement
from bcf.v2 import data as bcf
viewpoint = bcf.Viewpoint()
viewpoint.perspective_camera = bcf.PerspectiveCamera()
ifc_elem = requirement.failed_entities[0]
target_position = np.array(ifcopenshell.util.placement.get_local_placement(ifc_elem.ObjectPlacement))
target_position = target_position[:, 3][0:3]
camera_position = target_position + np.array((5, 5, 5))
viewpoint.perspective_camera.camera_view_point.x = camera_position[0]
viewpoint.perspective_camera.camera_view_point.y = camera_position[1]
viewpoint.perspective_camera.camera_view_point.z = camera_position[2]
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)
mat = np.linalg.inv(look_at_transform)
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]
viewpoint.components = bcf.Components()
for e in requirement.failed_entities:
c = bcf.Component()
c.ifc_guid = e.GlobalId
viewpoint.components.selection.append(c)
viewpoint.components.visibility = bcf.ComponentVisibility()
viewpoint.components.visibility.default_visibility = True
# add single pixel png file as a snapshot, as viewpoints without snapshots won't open in viewers
png = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x03\x00\x00\x00%\xdbV\xca\x00\x00\x00\x03PLTE\x00\x00\x00\xa7z=\xda\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00\nIDAT\x08\xd7c`\x00\x00\x00\x02\x00\x01\xe2!\xbc3\x00\x00\x00\x00IEND\xaeB`\x82"
snapshot_path = os.path.join(self.bcf.filepath, topic.guid, topic.guid+".png")
snapshot = open(snapshot_path, "wb")
snapshot.write(png)
snapshot.close()
viewpoint.snapshot = topic.guid+".png"
self.bcf.add_viewpoint(topic, viewpoint)
bcfxml = BcfXml.create_new(self.results["title"])
for specification in self.results["specifications"]:
if specification["status"]:
continue
for requirement in specification["requirements"]:
if requirement["status"]:
continue
for failure in requirement["failed_entities"]:
element = failure["element"]
title = f"{element.id()}/{element.is_a()}/"
title += getattr(element, "Name", None) or "Unnamed"
title += " - " + failure.get("reason", "No reason")
description = f'{specification["name"]} - {requirement["description"]}'
topic = bcfxml.add_topic(title, description, "IfcTester")
if element.is_a("IfcElement"):
topic.add_viewpoint(element)
bcfxml.save_project(filepath)