mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 18:57:17 +00:00
Merge pull request #3 from IfcOpenShell/v0.6.0
Merge changes from main repo
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
# bcf
|
||||||
|
|
||||||
|
A simple Python implementation of BCF. The data model is described in `data.py`.
|
||||||
|
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
||||||
|
is available via `bcfapi.py`.
|
||||||
|
|
||||||
|
Currently supports BCF version 2.1.
|
||||||
|
|
||||||
|
## bcfxml
|
||||||
|
|
||||||
|
The `bcfxml` module lets you interact with the BCF-XML standard.
|
||||||
|
|
||||||
|
```
|
||||||
|
from bcf.bcfxml import BcfXml
|
||||||
|
|
||||||
|
bcfxml = BcfXml()
|
||||||
|
|
||||||
|
# Load a project
|
||||||
|
project = bcfxml.get_project("/path/to/file.bcf")
|
||||||
|
|
||||||
|
# The project is also stored in the module
|
||||||
|
# project == bcfxml.project
|
||||||
|
|
||||||
|
print(project.name)
|
||||||
|
|
||||||
|
# To edit a project, just modify the object directly
|
||||||
|
bcfxml.project.name = "New name"
|
||||||
|
bcfxml.edit_project()
|
||||||
|
|
||||||
|
# The BCF file is extracted to this temporary directory
|
||||||
|
print(bcfxml.filepath)
|
||||||
|
|
||||||
|
# Get a dictionary of topics
|
||||||
|
topics = bcfxml.get_topics()
|
||||||
|
|
||||||
|
# Note: topics == bcfxml.topics
|
||||||
|
for guid, topic in bcfxml.topics.items():
|
||||||
|
print("Topic guid is", guid)
|
||||||
|
print("Topic guid is", topic.guid)
|
||||||
|
print("Topic title is", topic.title)
|
||||||
|
|
||||||
|
# Fetch extra data about a topic
|
||||||
|
header = bcfxml.get_header(guid)
|
||||||
|
comments = bcfxml.get_comments(guid)
|
||||||
|
viewpoints = bcfxml.get_viewpoints(guid)
|
||||||
|
|
||||||
|
# Note: comments == topic.comments, and so on
|
||||||
|
for comment_guid, comment in comments.items():
|
||||||
|
print(comment_guid)
|
||||||
|
print(comment.comment)
|
||||||
|
print(comment.author)
|
||||||
|
|
||||||
|
# Get a particular topic
|
||||||
|
topic = bcfxml.get_topic(guid)
|
||||||
|
|
||||||
|
# Modify a topic
|
||||||
|
topic.title = "New title"
|
||||||
|
bcfxml.edit_topic(topic)
|
||||||
|
```
|
||||||
@@ -0,0 +1,675 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import logging
|
||||||
|
import tempfile
|
||||||
|
import bcf.data
|
||||||
|
from datetime import datetime
|
||||||
|
from xml.dom import minidom
|
||||||
|
from xmlschema import XMLSchema
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
|
||||||
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def cd(newdir):
|
||||||
|
prevdir = os.getcwd()
|
||||||
|
os.chdir(os.path.expanduser(newdir))
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir(prevdir)
|
||||||
|
|
||||||
|
|
||||||
|
class BcfXml:
|
||||||
|
def __init__(self):
|
||||||
|
self.filepath = None
|
||||||
|
self.logger = logging.getLogger("bcfxml")
|
||||||
|
self.author = "john@doe.com"
|
||||||
|
self.project = bcf.data.Project()
|
||||||
|
self.version = "2.1"
|
||||||
|
self.topics = {}
|
||||||
|
|
||||||
|
def new_project(self):
|
||||||
|
self.project.project_id = str(uuid.uuid4())
|
||||||
|
self.project.name = "New Project"
|
||||||
|
self.topics = {}
|
||||||
|
if self.filepath:
|
||||||
|
self.close_project()
|
||||||
|
self.filepath = tempfile.mkdtemp()
|
||||||
|
self.edit_project()
|
||||||
|
self.edit_version()
|
||||||
|
|
||||||
|
def get_project(self, filepath=None):
|
||||||
|
if not filepath:
|
||||||
|
return self.project
|
||||||
|
zip_file = zipfile.ZipFile(filepath)
|
||||||
|
self.filepath = tempfile.mkdtemp()
|
||||||
|
zip_file.extractall(self.filepath)
|
||||||
|
data = self._read_xml("project.bcfp", "project.xsd")
|
||||||
|
self.project.project_id = data["Project"]["@ProjectId"]
|
||||||
|
self.project.name = data["Project"]["Name"]
|
||||||
|
return self.project
|
||||||
|
|
||||||
|
def edit_project(self):
|
||||||
|
self.document = minidom.Document()
|
||||||
|
root = self._create_element(self.document, "ProjectExtension")
|
||||||
|
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
|
||||||
|
self._create_element(project, "Name", text=self.project.name)
|
||||||
|
self._create_element(root, "ExtensionSchema", text="extensions.xsd")
|
||||||
|
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
|
||||||
|
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||||
|
|
||||||
|
def save_project(self, filepath):
|
||||||
|
with cd(self.filepath):
|
||||||
|
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
|
||||||
|
for root, dirs, files in os.walk("./"):
|
||||||
|
for file in files:
|
||||||
|
zip_file.write(os.path.join(root, file))
|
||||||
|
zip_file.close()
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
data = self._read_xml("bcf.version", "version.xsd")
|
||||||
|
self.version = data["@VersionId"]
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
def edit_version(self):
|
||||||
|
self.document = minidom.Document()
|
||||||
|
root = self._create_element(self.document, "Version", {"VersionId": self.version})
|
||||||
|
version = self._create_element(root, "DetailedVersion", text=self.version)
|
||||||
|
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
|
||||||
|
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||||
|
|
||||||
|
def get_topics(self):
|
||||||
|
self.topics = {}
|
||||||
|
topics = []
|
||||||
|
subdirs = []
|
||||||
|
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
|
||||||
|
subdirs = dirnames
|
||||||
|
break
|
||||||
|
for subdir in subdirs:
|
||||||
|
self.topics[subdir] = self.get_topic(subdir)
|
||||||
|
return self.topics
|
||||||
|
|
||||||
|
def get_header(self, guid):
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
if "Header" not in data:
|
||||||
|
return
|
||||||
|
header = bcf.data.Header()
|
||||||
|
for item in data["Header"]["File"]:
|
||||||
|
header_file = bcf.data.HeaderFile()
|
||||||
|
optional_keys = {
|
||||||
|
"filename": "Filename",
|
||||||
|
"date": "Date",
|
||||||
|
"reference": "Reference",
|
||||||
|
"ifc_project": "@IfcProject",
|
||||||
|
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
|
||||||
|
"is_external": "@isExternal",
|
||||||
|
}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in item:
|
||||||
|
setattr(header_file, key, item[value])
|
||||||
|
header.files.append(header_file)
|
||||||
|
self.topics[guid].header = header
|
||||||
|
return header
|
||||||
|
|
||||||
|
def get_topic(self, guid):
|
||||||
|
if guid in self.topics:
|
||||||
|
return self.topics[guid]
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
topic = bcf.data.Topic()
|
||||||
|
self.topics[guid] = topic
|
||||||
|
|
||||||
|
mandatory_keys = {
|
||||||
|
"guid": "@Guid",
|
||||||
|
"title": "Title",
|
||||||
|
"creation_date": "CreationDate",
|
||||||
|
"creation_author": "CreationAuthor",
|
||||||
|
}
|
||||||
|
for key, value in mandatory_keys.items():
|
||||||
|
setattr(topic, key, data["Topic"][value])
|
||||||
|
|
||||||
|
optional_keys = {
|
||||||
|
"priority": "Priority",
|
||||||
|
"index": "Index",
|
||||||
|
"labels": "Labels",
|
||||||
|
"reference_links": "ReferenceLink",
|
||||||
|
"modified_date": "ModifiedDate",
|
||||||
|
"modified_author": "ModifiedAuthor",
|
||||||
|
"due_date": "DueDate",
|
||||||
|
"assigned_to": "AssignedTo",
|
||||||
|
"stage": "Stage",
|
||||||
|
"description": "Description",
|
||||||
|
"topic_status": "@TopicStatus",
|
||||||
|
"topic_type": "@TopicType",
|
||||||
|
}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in data["Topic"]:
|
||||||
|
setattr(topic, key, data["Topic"][value])
|
||||||
|
|
||||||
|
if "BimSnippet" in data["Topic"]:
|
||||||
|
bim_snippet = bcf.data.BimSnippet()
|
||||||
|
keys = {
|
||||||
|
"snippet_type": "@SnippetType",
|
||||||
|
"is_external": "@IsExternal",
|
||||||
|
"reference": "Reference",
|
||||||
|
"reference_schema": "ReferenceSchema",
|
||||||
|
}
|
||||||
|
for key, value in keys.items():
|
||||||
|
if value in data["Topic"]["BimSnippet"]:
|
||||||
|
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
|
||||||
|
topic.bim_snippet = bim_snippet
|
||||||
|
|
||||||
|
if "DocumentReference" in data["Topic"]:
|
||||||
|
for item in data["Topic"]["DocumentReference"]:
|
||||||
|
document_reference = bcf.data.DocumentReference()
|
||||||
|
keys = {
|
||||||
|
"referenced_document": "ReferencedDocument",
|
||||||
|
"is_external": "@IsExternal",
|
||||||
|
"guid": "@Guid",
|
||||||
|
"description": "Description",
|
||||||
|
}
|
||||||
|
for key, value in keys.items():
|
||||||
|
if value in item:
|
||||||
|
setattr(document_reference, key, item[value])
|
||||||
|
topic.document_references.append(document_reference)
|
||||||
|
|
||||||
|
if "RelatedTopic" in data["Topic"]:
|
||||||
|
for item in data["Topic"]["RelatedTopic"]:
|
||||||
|
related_topic = bcf.data.RelatedTopic()
|
||||||
|
related_topic.guid = item["@Guid"]
|
||||||
|
topic.related_topics.append(related_topic)
|
||||||
|
return topic
|
||||||
|
|
||||||
|
def add_topic(self, topic=None):
|
||||||
|
if topic is None:
|
||||||
|
topic = bcf.data.Topic()
|
||||||
|
if not topic.guid:
|
||||||
|
topic.guid = str(uuid.uuid4())
|
||||||
|
if not topic.title:
|
||||||
|
topic.title = "New Topic"
|
||||||
|
os.mkdir(os.path.join(self.filepath, topic.guid))
|
||||||
|
self.edit_topic(topic)
|
||||||
|
return topic
|
||||||
|
|
||||||
|
def edit_topic(self, topic):
|
||||||
|
if not topic.creation_date:
|
||||||
|
topic.creation_date = datetime.utcnow().isoformat()
|
||||||
|
topic.creation_author = self.author
|
||||||
|
else:
|
||||||
|
topic.modified_date = datetime.utcnow().isoformat()
|
||||||
|
topic.modified_author = self.author
|
||||||
|
|
||||||
|
self.document = minidom.Document()
|
||||||
|
root = self._create_element(self.document, "Markup")
|
||||||
|
|
||||||
|
topic_el = self._create_element(
|
||||||
|
root,
|
||||||
|
"Topic",
|
||||||
|
{
|
||||||
|
"Guid": topic.guid,
|
||||||
|
"TopicType": topic.topic_type,
|
||||||
|
"TopicStatus": topic.topic_status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
text_map = {
|
||||||
|
"Title": topic.title,
|
||||||
|
"Priority": topic.priority,
|
||||||
|
"Index": topic.index,
|
||||||
|
"CreationDate": topic.creation_date,
|
||||||
|
"CreationAuthor": topic.creation_author,
|
||||||
|
"ModifiedDate": topic.modified_date,
|
||||||
|
"ModifiedAuthor": topic.modified_author,
|
||||||
|
"DueDate": topic.due_date,
|
||||||
|
"AssignedTo": topic.assigned_to,
|
||||||
|
"Stage": topic.stage,
|
||||||
|
"Description": topic.description,
|
||||||
|
}
|
||||||
|
for key, value in text_map.items():
|
||||||
|
if value:
|
||||||
|
self._create_element(topic_el, key, text=value)
|
||||||
|
|
||||||
|
for reference_link in topic.reference_links:
|
||||||
|
self._create_element(topic_el, "ReferenceLink", text=reference_link)
|
||||||
|
for label in topic.labels:
|
||||||
|
self._create_element(topic_el, "Labels", text=label)
|
||||||
|
if topic.bim_snippet:
|
||||||
|
bim_snippet = self._create_element(
|
||||||
|
topic_el,
|
||||||
|
"BimSnippet",
|
||||||
|
{"SnippetType": topic.bim_snippet.snippet_type, "isExternal": topic.bim_snippet.is_external},
|
||||||
|
)
|
||||||
|
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
|
||||||
|
self._create_element(bim_snippet, "ReferenceSchema", text=topic.bim_snippet.reference_schema)
|
||||||
|
for reference in topic.document_references:
|
||||||
|
reference_el = self._create_element(
|
||||||
|
topic_el, "DocumentReference", {"Guid": reference.guid, "isExternal": reference.is_external}
|
||||||
|
)
|
||||||
|
self._create_element(reference_el, "ReferencedDocument", text=reference.referenced_document)
|
||||||
|
self._create_element(reference_el, "Description", text=reference.description)
|
||||||
|
for related_topic in topic.related_topics:
|
||||||
|
self._create_element(topic_el, "RelatedTopic", {"Guid": related_topic.guid})
|
||||||
|
|
||||||
|
self.write_header(topic.header, root)
|
||||||
|
self.write_comments(topic.comments, root)
|
||||||
|
self.write_viewpoints(topic.viewpoints, root, topic)
|
||||||
|
|
||||||
|
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
|
||||||
|
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||||
|
|
||||||
|
def write_header(self, header, root):
|
||||||
|
if not header:
|
||||||
|
return
|
||||||
|
header_el = self._create_element(root, "Header")
|
||||||
|
for f in header.files:
|
||||||
|
file_el = self._create_element(
|
||||||
|
header_el,
|
||||||
|
"File",
|
||||||
|
{
|
||||||
|
"IfcProject": f.ifc_project,
|
||||||
|
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
|
||||||
|
"isExternal": f.is_external,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self._create_element(file_el, "Filename", text=f.filename)
|
||||||
|
self._create_element(file_el, "Date", text=f.date)
|
||||||
|
self._create_element(file_el, "Reference", text=f.reference)
|
||||||
|
|
||||||
|
def write_comments(self, comments, root):
|
||||||
|
for comment in comments.values():
|
||||||
|
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
|
||||||
|
text_map = {
|
||||||
|
"Date": comment.date,
|
||||||
|
"Author": comment.author,
|
||||||
|
"Comment": comment.comment,
|
||||||
|
"ModifiedDate": comment.modified_date,
|
||||||
|
"ModifiedAuthor": comment.modified_author,
|
||||||
|
}
|
||||||
|
for key, value in text_map.items():
|
||||||
|
if value:
|
||||||
|
self._create_element(comment_el, key, text=value)
|
||||||
|
if comment.viewpoint:
|
||||||
|
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
|
||||||
|
|
||||||
|
def add_comment(self, comment=None):
|
||||||
|
if comment is None:
|
||||||
|
comment = bcf.data.Comment()
|
||||||
|
if not comment.guid:
|
||||||
|
comment.guid = str(uuid.uuid4())
|
||||||
|
if not comment.comment:
|
||||||
|
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
|
||||||
|
self.edit_comment(comment)
|
||||||
|
|
||||||
|
def edit_comment(self, comment, topic):
|
||||||
|
if not comment.date:
|
||||||
|
comment.date = datetime.utcnow().isoformat()
|
||||||
|
comment.author = self.author
|
||||||
|
else:
|
||||||
|
comment.modified_date = datetime.utcnow().isoformat()
|
||||||
|
comment.modified_author = self.author
|
||||||
|
self.edit_topic(topic)
|
||||||
|
|
||||||
|
def delete_comment(self, guid, topic):
|
||||||
|
if guid in topic.comments:
|
||||||
|
del topic.comments[guid]
|
||||||
|
self.edit_topic(topic)
|
||||||
|
|
||||||
|
def delete_topic(self, guid):
|
||||||
|
if guid in self.topics:
|
||||||
|
del self.topics[guid]
|
||||||
|
shutil.rmtree(os.path.join(self.filepath, guid))
|
||||||
|
|
||||||
|
def write_viewpoints(self, viewpoints, root, topic):
|
||||||
|
for viewpoint in viewpoints.values():
|
||||||
|
viewpoint_el = self._create_element(root, "Viewpoints", {"Guid": viewpoint.guid})
|
||||||
|
text_map = {"Viewpoint": viewpoint.viewpoint, "Snapshot": viewpoint.snapshot, "Index": viewpoint.index}
|
||||||
|
for key, value in text_map.items():
|
||||||
|
if value:
|
||||||
|
self._create_element(viewpoint_el, key, text=value)
|
||||||
|
self.write_viewpoint(viewpoint, topic)
|
||||||
|
|
||||||
|
def write_viewpoint(self, viewpoint, topic):
|
||||||
|
document = minidom.Document()
|
||||||
|
root = self._create_element(document, "VisualizationInfo")
|
||||||
|
self.write_viewpoint_components(viewpoint, root)
|
||||||
|
self.write_viewpoint_orthogonal_camera(viewpoint, root)
|
||||||
|
self.write_viewpoint_perspective_camera(viewpoint, root)
|
||||||
|
self.write_viewpoint_lines(viewpoint, root)
|
||||||
|
self.write_viewpoint_clipping_planes(viewpoint, root)
|
||||||
|
self.write_viewpoint_bitmaps(viewpoint, root)
|
||||||
|
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
|
||||||
|
f.write(document.toprettyxml(encoding="utf-8"))
|
||||||
|
|
||||||
|
def write_viewpoint_components(self, viewpoint, parent):
|
||||||
|
if not viewpoint.components:
|
||||||
|
return
|
||||||
|
components_el = self._create_element(parent, "Components")
|
||||||
|
if viewpoint.components.view_setup_hints:
|
||||||
|
view_setup_hints = self._create_element(
|
||||||
|
components_el,
|
||||||
|
"ViewSetupHints",
|
||||||
|
{
|
||||||
|
"SpacesVisible": viewpoint.components.view_setup_hints.spaces_visible,
|
||||||
|
"SpaceBoundiresVisible": viewpoint.components.view_setup_hints.space_boundaries_visible,
|
||||||
|
"OpeningsVisible": viewpoint.components.view_setup_hints.openings_visible,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if viewpoint.components.selection:
|
||||||
|
selection_el = self._create_element(components_el, "Selection")
|
||||||
|
for selection in viewpoint.components.selection:
|
||||||
|
self.write_component(selection, selection_el)
|
||||||
|
visibility = self._create_element(
|
||||||
|
components_el, "Visibility", {"DefaultVisibility": viewpoint.components.visibility.default_visibility}
|
||||||
|
)
|
||||||
|
if viewpoint.components.visibility.exceptions:
|
||||||
|
exceptions_el = self._create_element(visibility, "Exceptions")
|
||||||
|
for exception in viewpoint.components.visibility.exceptions:
|
||||||
|
self.write_component(exception, exceptions_el)
|
||||||
|
if viewpoint.components.coloring:
|
||||||
|
coloring_el = self._create_element(components_el, "Coloring")
|
||||||
|
for color in viewpoint.components.coloring:
|
||||||
|
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
|
||||||
|
for component in color.components:
|
||||||
|
self.write_component(component, color_el)
|
||||||
|
|
||||||
|
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
|
||||||
|
if not viewpoint.orthogonal_camera:
|
||||||
|
return
|
||||||
|
camera = viewpoint.orthogonal_camera
|
||||||
|
camera_el = self._create_element(parent, "OrthogonalCamera")
|
||||||
|
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
|
||||||
|
self.write_vector(camera_view_point, camera.camera_view_point)
|
||||||
|
camera_direction = self._create_element(camera_el, "CameraDirection")
|
||||||
|
self.write_vector(camera_direction, camera.camera_direction)
|
||||||
|
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
|
||||||
|
self.write_vector(camera_up_vector, camera.camera_up_vector)
|
||||||
|
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
|
||||||
|
|
||||||
|
def write_viewpoint_perspective_camera(self, viewpoint, parent):
|
||||||
|
if not viewpoint.perspective_camera:
|
||||||
|
return
|
||||||
|
camera = viewpoint.perspective_camera
|
||||||
|
camera_el = self._create_element(parent, "PerspectiveCamera")
|
||||||
|
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
|
||||||
|
self.write_vector(camera_view_point, camera.camera_view_point)
|
||||||
|
camera_direction = self._create_element(camera_el, "CameraDirection")
|
||||||
|
self.write_vector(camera_direction, camera.camera_direction)
|
||||||
|
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
|
||||||
|
self.write_vector(camera_up_vector, camera.camera_up_vector)
|
||||||
|
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
|
||||||
|
|
||||||
|
def write_viewpoint_lines(self, viewpoint, parent):
|
||||||
|
if not viewpoint.lines:
|
||||||
|
return
|
||||||
|
lines_el = self._create_element(parent, "Lines")
|
||||||
|
for line in viewpoint.lines:
|
||||||
|
line_el = self._create_element(lines_el, "Line")
|
||||||
|
start_point_el = self._create_element(line_el, "StartPoint")
|
||||||
|
self.write_vector(start_point_el, line.start_point)
|
||||||
|
end_point_el = self._create_element(line_el, "EndPoint")
|
||||||
|
self.write_vector(end_point_el, line.end_point)
|
||||||
|
|
||||||
|
def write_viewpoint_clipping_planes(self, viewpoint, parent):
|
||||||
|
if not viewpoint.clipping_planes:
|
||||||
|
return
|
||||||
|
planes_el = self._create_element(parent, "ClippingPlanes")
|
||||||
|
for plane in viewpoint.clipping_planes:
|
||||||
|
plane_el = self._create_element(planes_el, "ClippingPlane")
|
||||||
|
location_el = self._create_element(plane_el, "Location")
|
||||||
|
self.write_vector(location_el, plane.location)
|
||||||
|
direction_el = self._create_element(plane_el, "Direction")
|
||||||
|
self.write_vector(direction_el, plane.direction)
|
||||||
|
|
||||||
|
def write_viewpoint_bitmaps(self, viewpoint, parent):
|
||||||
|
if not viewpoint.bitmaps:
|
||||||
|
return
|
||||||
|
for bitmap in viewpoint.bitmaps:
|
||||||
|
bitmap_el = self._create_element(parent, "Bitmap")
|
||||||
|
text_map = {"Bitmap": bitmap.bitmap_type, "Reference": bitmap.reference, "Height": bitmap.height}
|
||||||
|
for key, value in text_map.items():
|
||||||
|
self._create_element(bitmap_el, key, text=value)
|
||||||
|
location_el = self._create_element(bitmap_el, "Location")
|
||||||
|
self.write_vector(location_el, bitmap.location)
|
||||||
|
normal_el = self._create_element(bitmap_el, "Normal")
|
||||||
|
self.write_vector(normal_el, bitmap.normal)
|
||||||
|
up_el = self._create_element(bitmap_el, "Up")
|
||||||
|
self.write_vector(up_el, bitmap.up)
|
||||||
|
|
||||||
|
def write_vector(self, parent, from_obj):
|
||||||
|
self._create_element(parent, "X", text=from_obj.x)
|
||||||
|
self._create_element(parent, "Y", text=from_obj.y)
|
||||||
|
self._create_element(parent, "Z", text=from_obj.z)
|
||||||
|
|
||||||
|
def write_component(self, data, parent):
|
||||||
|
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
|
||||||
|
text_map = {"OriginatingSystem": data.originating_system, "AuthoringToolId": data.authoring_tool_id}
|
||||||
|
for key, value in text_map.items():
|
||||||
|
if value:
|
||||||
|
self._create_element(component_el, key, text=value)
|
||||||
|
|
||||||
|
def add_viewpoint(self, topic, viewpoint=None):
|
||||||
|
if not viewpoint:
|
||||||
|
viewpoint = bcf.data.Viewpoint()
|
||||||
|
if not viewpoint.guid:
|
||||||
|
viewpoint.guid = str(uuid.uuid4())
|
||||||
|
if not viewpoint.viewpoint:
|
||||||
|
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
|
||||||
|
if viewpoint.snapshot:
|
||||||
|
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||||
|
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
pass # TODO: handle uploading files
|
||||||
|
self.edit_topic(topic)
|
||||||
|
|
||||||
|
def delete_viewpoint(self, guid, topic):
|
||||||
|
if guid not in topic.viewpoints:
|
||||||
|
return
|
||||||
|
viewpoint = topic.viewpoints[guid]
|
||||||
|
if viewpoint.snapshot:
|
||||||
|
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
if viewpoint.viewpoint:
|
||||||
|
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
for bitmap in viewpoint.bitmaps:
|
||||||
|
if not bitmap.reference:
|
||||||
|
continue
|
||||||
|
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
del topic.viewpoints[guid]
|
||||||
|
self.edit_topic(topic)
|
||||||
|
|
||||||
|
def get_comments(self, guid):
|
||||||
|
comments = {}
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
if "Comment" not in data:
|
||||||
|
return comments
|
||||||
|
for item in data["Comment"]:
|
||||||
|
comment = bcf.data.Comment()
|
||||||
|
mandatory_keys = {"guid": "@Guid", "date": "Date", "author": "Author", "comment": "Comment"}
|
||||||
|
for key, value in mandatory_keys.items():
|
||||||
|
setattr(comment, key, item[value])
|
||||||
|
optional_keys = {"modified_date": "ModifiedDate", "modified_author": "ModifiedAuthor"}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in item:
|
||||||
|
setattr(comment, key, item[value])
|
||||||
|
if "Viewpoint" in item:
|
||||||
|
viewpoint = bcf.data.Viewpoint()
|
||||||
|
viewpoint.guid = item["Viewpoint"]["@Guid"]
|
||||||
|
comment.viewpoint = viewpoint
|
||||||
|
comments[comment.guid] = comment
|
||||||
|
self.topics[guid].comments = comments
|
||||||
|
return comments
|
||||||
|
|
||||||
|
def get_viewpoints(self, guid):
|
||||||
|
viewpoints = {}
|
||||||
|
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||||
|
if "Viewpoints" not in data:
|
||||||
|
return viewpoints
|
||||||
|
for item in data["Viewpoints"]:
|
||||||
|
viewpoint = self.get_viewpoint(item, guid)
|
||||||
|
viewpoints[viewpoint.guid] = viewpoint
|
||||||
|
self.topics[guid].viewpoints = viewpoints
|
||||||
|
return viewpoints
|
||||||
|
|
||||||
|
def get_viewpoint(self, data, topic_guid):
|
||||||
|
viewpoint = bcf.data.Viewpoint()
|
||||||
|
viewpoint.guid = data["@Guid"]
|
||||||
|
optional_keys = {"viewpoint": "Viewpoint", "snapshot": "Snapshot", "index": "Index"}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in data:
|
||||||
|
setattr(viewpoint, key, data[value])
|
||||||
|
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
|
||||||
|
viewpoint.components = self.get_viewpoint_components(visinfo)
|
||||||
|
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
|
||||||
|
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
|
||||||
|
viewpoint.lines = self.get_viewpoint_lines(visinfo)
|
||||||
|
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
|
||||||
|
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
|
||||||
|
return viewpoint
|
||||||
|
|
||||||
|
def get_viewpoint_components(self, visinfo):
|
||||||
|
if "Components" not in visinfo:
|
||||||
|
return None
|
||||||
|
components = bcf.data.Components()
|
||||||
|
data = visinfo["Components"]
|
||||||
|
if "ViewSetupHints" in data:
|
||||||
|
view_setup_hints = bcf.data.ViewSetupHints()
|
||||||
|
optional_keys = {
|
||||||
|
"spaces_visible": "@SpacesVisible",
|
||||||
|
"space_boundaries_visible": "@SpaceBoundariesVisible",
|
||||||
|
"openings_visible": "@OpeningsVisible",
|
||||||
|
}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in data["ViewSetupHints"]:
|
||||||
|
setattr(view_setup_hints, key, data["ViewSetupHints"][value])
|
||||||
|
components.view_setup_hints = view_setup_hints
|
||||||
|
if "Selection" in data and "Component" in data["Selection"]:
|
||||||
|
for item in data["Selection"]["Component"]:
|
||||||
|
components.selection.append(self.get_component(item))
|
||||||
|
if "Visibility" in data:
|
||||||
|
component_visibility = bcf.data.ComponentVisibility()
|
||||||
|
if "@DefaultVisibility" in data["Visibility"]:
|
||||||
|
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
|
||||||
|
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
|
||||||
|
for item in data["Visibility"]["Exceptions"]["Component"]:
|
||||||
|
component_visibility.exceptions.append(self.get_component(item))
|
||||||
|
components.visibility = component_visibility
|
||||||
|
if "Coloring" in data and "Color" in data["Coloring"]:
|
||||||
|
for item in data["Coloring"]["Color"]:
|
||||||
|
color = bcf.data.Color()
|
||||||
|
color.color = item["@Color"]
|
||||||
|
for item2 in item["Component"]:
|
||||||
|
color.components.append(self.get_component(item2))
|
||||||
|
components.coloring.append(color)
|
||||||
|
return components
|
||||||
|
|
||||||
|
def get_viewpoint_orthogonal_camera(self, visinfo):
|
||||||
|
if "OrthogonalCamera" not in visinfo:
|
||||||
|
return None
|
||||||
|
camera = bcf.data.OrthogonalCamera()
|
||||||
|
data = visinfo["OrthogonalCamera"]
|
||||||
|
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
|
||||||
|
self.set_vector(camera.camera_direction, data["CameraDirection"])
|
||||||
|
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
|
||||||
|
camera.view_to_world_scale = data["ViewToWorldScale"]
|
||||||
|
return camera
|
||||||
|
|
||||||
|
def get_viewpoint_perspective_camera(self, visinfo):
|
||||||
|
if "PerspectiveCamera" not in visinfo:
|
||||||
|
return None
|
||||||
|
camera = bcf.data.PerspectiveCamera()
|
||||||
|
data = visinfo["PerspectiveCamera"]
|
||||||
|
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
|
||||||
|
self.set_vector(camera.camera_direction, data["CameraDirection"])
|
||||||
|
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
|
||||||
|
camera.field_of_view = data["FieldOfView"]
|
||||||
|
return camera
|
||||||
|
|
||||||
|
def get_viewpoint_lines(self, visinfo):
|
||||||
|
if "Lines" not in visinfo:
|
||||||
|
return []
|
||||||
|
lines = []
|
||||||
|
for item in visinfo["Lines"]["Line"]:
|
||||||
|
line = bcf.data.Line()
|
||||||
|
self.set_vector(line.start_point, item["StartPoint"])
|
||||||
|
self.set_vector(line.end_point, item["EndPoint"])
|
||||||
|
lines.append(line)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def get_viewpoint_clipping_planes(self, visinfo):
|
||||||
|
if "ClippingPlanes" not in visinfo:
|
||||||
|
return []
|
||||||
|
planes = []
|
||||||
|
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
|
||||||
|
plane = bcf.data.ClippingPlane()
|
||||||
|
self.set_vector(plane.location, item["Location"])
|
||||||
|
self.set_vector(plane.direction, item["Direction"])
|
||||||
|
planes.append(plane)
|
||||||
|
return planes
|
||||||
|
|
||||||
|
def get_viewpoint_bitmaps(self, visinfo):
|
||||||
|
if "Bitmap" not in visinfo:
|
||||||
|
return []
|
||||||
|
bitmaps = []
|
||||||
|
for item in visinfo["Bitmap"]:
|
||||||
|
bitmap = bcf.data.Bitmap()
|
||||||
|
bitmap.reference = item["Reference"]
|
||||||
|
bitmap.bitmap_type = item["Bitmap"].lower()
|
||||||
|
self.set_vector(bitmap.location, item["Location"])
|
||||||
|
self.set_vector(bitmap.normal, item["Normal"])
|
||||||
|
self.set_vector(bitmap.up, item["Up"])
|
||||||
|
bitmap.height = item["Height"]
|
||||||
|
bitmaps.append(bitmap)
|
||||||
|
return bitmaps
|
||||||
|
|
||||||
|
def set_vector(self, to_obj, from_xml):
|
||||||
|
to_obj.x = from_xml["X"]
|
||||||
|
to_obj.y = from_xml["Y"]
|
||||||
|
to_obj.z = from_xml["Z"]
|
||||||
|
|
||||||
|
def get_component(self, data):
|
||||||
|
component = bcf.data.Component()
|
||||||
|
optional_keys = {
|
||||||
|
"originating_system": "OriginatingSystem",
|
||||||
|
"authoring_tool_id": "AuthoringToolId",
|
||||||
|
"ifc_guid": "@IfcGuid",
|
||||||
|
}
|
||||||
|
for key, value in optional_keys.items():
|
||||||
|
if value in data:
|
||||||
|
setattr(component, key, data[value])
|
||||||
|
return component
|
||||||
|
|
||||||
|
def close_project(self):
|
||||||
|
shutil.rmtree(self.filepath)
|
||||||
|
|
||||||
|
def _read_xml(self, filename, xsd):
|
||||||
|
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
|
||||||
|
filepath = os.path.join(self.filepath, filename)
|
||||||
|
(data, errors) = schema.to_dict(filepath, validation="lax")
|
||||||
|
for error in errors:
|
||||||
|
self.logger.error(error)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _create_element(self, parent, name, attributes={}, text=None):
|
||||||
|
element = self.document.createElement(name)
|
||||||
|
for key, value in attributes.items():
|
||||||
|
if isinstance(value, bool):
|
||||||
|
element.setAttribute(key, str(value).lower())
|
||||||
|
elif value:
|
||||||
|
element.setAttribute(key, value)
|
||||||
|
if text is not None:
|
||||||
|
text = self.document.createTextNode(str(text))
|
||||||
|
element.appendChild(text)
|
||||||
|
parent.appendChild(element)
|
||||||
|
return element
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close_project()
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
class Project:
|
||||||
|
def __init__(self):
|
||||||
|
self.project_id = ""
|
||||||
|
self.name = ""
|
||||||
|
|
||||||
|
|
||||||
|
class BimSnippet:
|
||||||
|
def __init__(self):
|
||||||
|
self.snippet_type = None
|
||||||
|
self.is_external = False
|
||||||
|
self.reference = None
|
||||||
|
self.reference_schema = None
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentReference:
|
||||||
|
def __init__(self):
|
||||||
|
self.referenced_document = None
|
||||||
|
self.description = None
|
||||||
|
self.guid = None
|
||||||
|
self.is_external = False
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedTopic:
|
||||||
|
def __init__(self):
|
||||||
|
self.guid = None
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderFile:
|
||||||
|
def __init__(self):
|
||||||
|
self.filename = None
|
||||||
|
self.date = None
|
||||||
|
self.reference = None
|
||||||
|
self.ifc_project = None
|
||||||
|
self.ifc_spatial_structure_element = None
|
||||||
|
self.is_external = True
|
||||||
|
|
||||||
|
|
||||||
|
class Header:
|
||||||
|
def __init__(self):
|
||||||
|
self.files = []
|
||||||
|
|
||||||
|
|
||||||
|
class Topic:
|
||||||
|
def __init__(self):
|
||||||
|
self.reference_links = []
|
||||||
|
self.title = ""
|
||||||
|
self.priority = None
|
||||||
|
self.index = None # Deprecated, stored, but ignored
|
||||||
|
self.labels = []
|
||||||
|
self.creation_date = None
|
||||||
|
self.creation_author = None
|
||||||
|
self.modified_date = None
|
||||||
|
self.modified_author = None
|
||||||
|
self.due_date = None
|
||||||
|
self.assigned_to = None
|
||||||
|
self.stage = None
|
||||||
|
self.description = None
|
||||||
|
self.bim_snippet = None
|
||||||
|
self.document_references = []
|
||||||
|
self.related_topics = []
|
||||||
|
self.topic_status = None
|
||||||
|
self.topic_type = None
|
||||||
|
self.guid = None
|
||||||
|
|
||||||
|
self.header = None
|
||||||
|
self.comments = {}
|
||||||
|
self.viewpoints = {}
|
||||||
|
|
||||||
|
|
||||||
|
class Comment:
|
||||||
|
def __init__(self):
|
||||||
|
self.guid = None
|
||||||
|
self.date = None
|
||||||
|
self.author = None
|
||||||
|
self.comment = None
|
||||||
|
self.viewpoint = None
|
||||||
|
self.modified_date = None
|
||||||
|
self.modified_author = None
|
||||||
|
self.topic_guid = None # Part of BCF-API
|
||||||
|
|
||||||
|
|
||||||
|
class ViewSetupHints:
|
||||||
|
def __init__(self):
|
||||||
|
self.spaces_visible = False
|
||||||
|
self.space_boundaries_visible = False
|
||||||
|
self.openings_visible = False
|
||||||
|
|
||||||
|
|
||||||
|
class Component:
|
||||||
|
def __init__(self):
|
||||||
|
self.originating_system = None
|
||||||
|
self.authoring_tool_id = None
|
||||||
|
self.ifc_guid = None
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentVisibility:
|
||||||
|
def __init__(self):
|
||||||
|
self.exceptions = []
|
||||||
|
self.default_visibility = False
|
||||||
|
|
||||||
|
|
||||||
|
class Color:
|
||||||
|
def __init__(self):
|
||||||
|
self.color = None
|
||||||
|
self.components = []
|
||||||
|
|
||||||
|
|
||||||
|
class Components:
|
||||||
|
def __init__(self):
|
||||||
|
self.view_setup_hints = None
|
||||||
|
self.selection = []
|
||||||
|
self.visibility = None
|
||||||
|
self.coloring = []
|
||||||
|
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
def __init__(self):
|
||||||
|
self.x = 0
|
||||||
|
self.y = 0
|
||||||
|
self.z = 0
|
||||||
|
|
||||||
|
|
||||||
|
class Direction(Point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OrthogonalCamera:
|
||||||
|
def __init__(self):
|
||||||
|
self.camera_view_point = Point()
|
||||||
|
self.camera_direction = Direction()
|
||||||
|
self.camera_up_vector = Direction()
|
||||||
|
self.view_to_world_scale = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
class PerspectiveCamera:
|
||||||
|
def __init__(self):
|
||||||
|
self.camera_view_point = Point()
|
||||||
|
self.camera_direction = Direction()
|
||||||
|
self.camera_up_vector = Direction()
|
||||||
|
self.field_of_view = 60.0
|
||||||
|
|
||||||
|
|
||||||
|
class Line:
|
||||||
|
def __init__(self):
|
||||||
|
self.start_point = Point()
|
||||||
|
self.end_point = Point()
|
||||||
|
|
||||||
|
|
||||||
|
class ClippingPlane:
|
||||||
|
def __init__(self):
|
||||||
|
self.location = Point()
|
||||||
|
self.direction = Direction()
|
||||||
|
|
||||||
|
|
||||||
|
class Bitmap:
|
||||||
|
def __init__(self):
|
||||||
|
self.reference = "" # Only in BCF-XML
|
||||||
|
self.bitmap_data = None # Only in BCF-API
|
||||||
|
self.bitmap_type = "png" # Enum of png or jpg
|
||||||
|
self.location = Point()
|
||||||
|
self.normal = Direction()
|
||||||
|
self.up = Direction()
|
||||||
|
self.height = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
class Viewpoint:
|
||||||
|
def __init__(self):
|
||||||
|
self.guid = None
|
||||||
|
self.viewpoint = None
|
||||||
|
self.snapshot = None
|
||||||
|
self.index = None
|
||||||
|
|
||||||
|
self.components = None # It's not a list, despite the plural name
|
||||||
|
self.orthogonal_camera = None
|
||||||
|
self.perspective_camera = None
|
||||||
|
self.lines = []
|
||||||
|
self.clipping_planes = []
|
||||||
|
self.bitmaps = []
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="Markup">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Header" type="Header" minOccurs="0"/>
|
||||||
|
<xs:element name="Topic" type="Topic"/>
|
||||||
|
<xs:element name="Comment" type="Comment" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
|
||||||
|
<xs:element name="Viewpoints" type="ViewPoint" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="Header">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="File" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Filename" type="xs:string" minOccurs="0"/>
|
||||||
|
<xs:element name="Date" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<!-- Reference (URL) of the file -->
|
||||||
|
<xs:element name="Reference" type="xs:string" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attributeGroup ref="FileAttributes"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
|
||||||
|
<xs:complexType name="ViewPoint">
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- viewpoint file (xml) -->
|
||||||
|
<xs:element name="Viewpoint" type="xs:string" minOccurs="0"/>
|
||||||
|
<!-- the snapshot png -->
|
||||||
|
<xs:element name="Snapshot" type="xs:string" minOccurs="0"/>
|
||||||
|
<!-- the viewpoint index (sort order) -->
|
||||||
|
<xs:element name="Index" type="xs:int" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
<!-- Guid of the viewpoint -->
|
||||||
|
</xs:complexType>
|
||||||
|
<!-- BimSnippet -->
|
||||||
|
<xs:complexType name="BimSnippet">
|
||||||
|
<xs:sequence>
|
||||||
|
<!--
|
||||||
|
Name of the file in the topic folder containing the snippet or a URL.
|
||||||
|
E.G.- Expresscode containing p.e Issue, Request
|
||||||
|
// Maybe some header infos ?? // IfcEntites // Geometry
|
||||||
|
-->
|
||||||
|
<!-- Reference (name) to the snippet file -->
|
||||||
|
<xs:element name="Reference" type="xs:string"/>
|
||||||
|
<xs:element name="ReferenceSchema" type="xs:string"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="SnippetType" type="xs:string" use="required"/>
|
||||||
|
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
|
||||||
|
<!-- This flag is true when the reference is a URL pointing outside of the BCF file-->
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="Topic">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ReferenceLink" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
<xs:element name="Title" type="xs:string"/>
|
||||||
|
<xs:element name="Priority" type="Priority" minOccurs="0"/>
|
||||||
|
<!-- ISG Jira issue BCF-8 Add a way save order the topics -->
|
||||||
|
<xs:element name="Index" type="xs:int" minOccurs="0"/>
|
||||||
|
<xs:element name="Labels" type="TopicLabel" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
<xs:element name="CreationDate" type="xs:dateTime" minOccurs="1"/>
|
||||||
|
<xs:element name="CreationAuthor" type="UserIdType" minOccurs="1"/>
|
||||||
|
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
|
||||||
|
<xs:element name="DueDate" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<xs:element name="AssignedTo" type="UserIdType" minOccurs="0"/>
|
||||||
|
<xs:element name="Stage" type="Stage" minOccurs="0"/>
|
||||||
|
<xs:element name="Description" type="xs:string" minOccurs="0"/>
|
||||||
|
<xs:element name="BimSnippet" type="BimSnippet" minOccurs="0"/>
|
||||||
|
<!-- Name of the file in the topic folder or url -->
|
||||||
|
<xs:element name="DocumentReference" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- Name of the file in the topic folder or url -->
|
||||||
|
<xs:element name="ReferencedDocument" type="xs:string" minOccurs="0"/>
|
||||||
|
<!-- Human readable name of the document -->
|
||||||
|
<xs:element name="Description" type="xs:string" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attributeGroup ref="DocumentReference"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="RelatedTopic" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
<xs:attribute name="TopicType" type="TopicType"/>
|
||||||
|
<xs:attribute name="TopicStatus" type="TopicStatus"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<!-- Reference to a document inside of the topic folder or a url pointing to the web -->
|
||||||
|
<xs:attributeGroup name="DocumentReference">
|
||||||
|
<!-- Guid of the DocumentReference -->
|
||||||
|
<xs:attribute name="Guid" type="Guid"/>
|
||||||
|
<!-- A flag that is true when the ReferencedDocument points outside of the BCF file (a URL) -->
|
||||||
|
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
|
<xs:complexType name="Comment">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Date" type="xs:dateTime"/>
|
||||||
|
<xs:element name="Author" type="UserIdType"/>
|
||||||
|
<xs:element name="Comment" type="xs:string"/>
|
||||||
|
<xs:element name="Viewpoint" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
|
||||||
|
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:simpleType name="TopicStatus">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="TopicType">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="TopicLabel">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="Priority">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="UserIdType">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="Stage">
|
||||||
|
<xs:restriction base="xs:string"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="Guid">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="IfcGuid">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:length value="22"/>
|
||||||
|
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:attributeGroup name="FileAttributes">
|
||||||
|
<xs:attribute name="IfcProject" type="IfcGuid"/>
|
||||||
|
<xs:attribute name="IfcSpatialStructureElement" type="IfcGuid"/>
|
||||||
|
<xs:attribute name="isExternal" type="xs:boolean" default="true"/>
|
||||||
|
</xs:attributeGroup>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="ProjectExtension">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Project" type="Project" minOccurs="0"/>
|
||||||
|
<xs:element name="ExtensionSchema" type="xs:anyURI"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="Project">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Name" type="xs:string" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="ProjectId" type="xs:string" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Mit XMLSpy v2011 rel. 3 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="Version">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="DetailedVersion" type="xs:string" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="VersionId" type="xs:string"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||||
|
<xs:element name="VisualizationInfo">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>VisualizationInfo documentation</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Components" type="Components" minOccurs="0"/>
|
||||||
|
<xs:element name="OrthogonalCamera" type="OrthogonalCamera" minOccurs="0"/>
|
||||||
|
<xs:element name="PerspectiveCamera" type="PerspectiveCamera" minOccurs="0"/>
|
||||||
|
<xs:element name="Lines" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Line" type="Line" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="ClippingPlanes" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ClippingPlane" type="ClippingPlane" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
|
||||||
|
<xs:element name="Bitmap" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Bitmap" type="BitmapFormat"/>
|
||||||
|
<!-- Name of the bitmap file in the topic folder -->
|
||||||
|
<xs:element name="Reference" type="xs:string"/>
|
||||||
|
<!-- Location of the center of the bitmap -->
|
||||||
|
<xs:element name="Location" type="Point"/>
|
||||||
|
<!-- Normal of the bitmap -->
|
||||||
|
<xs:element name="Normal" type="Direction"/>
|
||||||
|
<!-- Upvector of the bitmap -->
|
||||||
|
<xs:element name="Up" type="Direction"/>
|
||||||
|
<!-- Height of the bitmap -->
|
||||||
|
<xs:element name="Height" type="xs:double"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<!-- Guid of the viewpoint -->
|
||||||
|
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:complexType name="OrthogonalCamera">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="CameraViewPoint" type="Point"/>
|
||||||
|
<xs:element name="CameraDirection" type="Direction"/>
|
||||||
|
<xs:element name="CameraUpVector" type="Direction"/>
|
||||||
|
<xs:element name="ViewToWorldScale" type="xs:double">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>view's visible size in meters</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="PerspectiveCamera">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="CameraViewPoint" type="Point"/>
|
||||||
|
<xs:element name="CameraDirection" type="Direction"/>
|
||||||
|
<xs:element name="CameraUpVector" type="Direction"/>
|
||||||
|
<xs:element name="FieldOfView" type="FieldOfView">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
It is currently limited to a value between 45 and 60 degrees.
|
||||||
|
This limitation will be dropped in the next release and viewers
|
||||||
|
should be expect values outside this range in current implementations.
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="Point">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="X" type="xs:double"/>
|
||||||
|
<xs:element name="Y" type="xs:double"/>
|
||||||
|
<xs:element name="Z" type="xs:double"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="Direction">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="X" type="xs:double"/>
|
||||||
|
<xs:element name="Y" type="xs:double"/>
|
||||||
|
<xs:element name="Z" type="xs:double"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:simpleType name="FieldOfView">
|
||||||
|
<xs:restriction base="xs:double">
|
||||||
|
<xs:minInclusive value="45"/>
|
||||||
|
<xs:maxInclusive value="60"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:complexType name="Components">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ViewSetupHints" type="ViewSetupHints" minOccurs="0" />
|
||||||
|
<!-- Components with relevance to the viewpoint. They should be displayed highlighted or selected in a viewer -->
|
||||||
|
<xs:element name="Selection" type="ComponentSelection" minOccurs="0" />
|
||||||
|
<xs:element name="Visibility" type="ComponentVisibility" minOccurs="1" />
|
||||||
|
<xs:element name="Coloring" type="ComponentColoring" minOccurs="0" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ViewSetupHints">
|
||||||
|
<xs:attribute name="SpacesVisible" type="xs:boolean"/>
|
||||||
|
<xs:attribute name="SpaceBoundariesVisible" type="xs:boolean"/>
|
||||||
|
<xs:attribute name="OpeningsVisible" type="xs:boolean"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentSelection">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentVisibility">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Exceptions" minOccurs="0">
|
||||||
|
<!-- List Components that are different than the DefaultVisibility. E.g. if DefaultVisibility = false then list
|
||||||
|
Components that should be visible -->
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="DefaultVisibility" type="xs:boolean"/>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ComponentColoring">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Color" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute ref="Color"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="Component">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="OriginatingSystem" type="xs:string" minOccurs="0"/>
|
||||||
|
<xs:element name="AuthoringToolId" type="xs:string" minOccurs="0"/>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute ref="IfcGuid"/>
|
||||||
|
<!-- ISG Jira Issue BCF-14 -->
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:attribute name="Color">
|
||||||
|
<xs:simpleType>
|
||||||
|
<xs:restriction base="xs:normalizedString">
|
||||||
|
<!-- Should either match 3 or 4 hex bytes , e.g. "FF00FF" or "FF00FF99" -->
|
||||||
|
<xs:pattern value="[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="IfcGuid">
|
||||||
|
<xs:simpleType>
|
||||||
|
<xs:restriction base="xs:normalizedString">
|
||||||
|
<xs:length value="22"/>
|
||||||
|
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:complexType name="Line">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="StartPoint" type="Point"/>
|
||||||
|
<xs:element name="EndPoint" type="Point"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:complexType name="ClippingPlane">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Location" type="Point"/>
|
||||||
|
<xs:element name="Direction" type="Direction"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
|
||||||
|
<xs:simpleType name="BitmapFormat">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:enumeration value="PNG"/>
|
||||||
|
<xs:enumeration value="JPG"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
<xs:simpleType name="Guid">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:schema>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import gettext
|
||||||
from behave import step
|
from behave import step
|
||||||
|
|
||||||
|
from ifcdata_methods import assert_schema
|
||||||
from utils import IfcFile
|
from utils import IfcFile
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
|
||||||
@step('The IFC file "{file}" must be provided')
|
@step('The IFC file "{file}" must be provided')
|
||||||
@@ -13,10 +16,9 @@ def step_impl(context, file):
|
|||||||
|
|
||||||
@step("IFC data must use the {schema} schema")
|
@step("IFC data must use the {schema} schema")
|
||||||
def step_impl(context, schema):
|
def step_impl(context, schema):
|
||||||
assert IfcFile.get().schema == schema, "We expected a schema of {} but instead got {}".format(
|
switch_locale(context.localedir, "en")
|
||||||
schema, IfcFile.get().schema
|
assert_schema(context, schema)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@step('The IFC file "{file}" is exempt from being provided')
|
@step('The IFC file "{file}" is exempt from being provided')
|
||||||
def step_impl(context, file):
|
def step_impl(context, file):
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from ifcdata_methods import assert_schema
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
@step("Die IFC Daten müssen das {schema} Schema benutzen")
|
||||||
|
def step_impl(context, schema):
|
||||||
|
switch_locale(context.localedir, "de")
|
||||||
|
assert_schema(context, schema)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from behave import step
|
||||||
|
|
||||||
|
from ifcdata_methods import assert_schema
|
||||||
|
from utils import switch_locale
|
||||||
|
|
||||||
|
@step("Les données IFC doivent utiliser le schéma {schema}")
|
||||||
|
def step_impl(context, schema):
|
||||||
|
switch_locale(context.localedir, "fr")
|
||||||
|
assert_schema(context, schema)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from utils import IfcFile
|
||||||
|
|
||||||
|
|
||||||
|
def assert_schema(context, target_schema):
|
||||||
|
real_schema = IfcFile.get().schema
|
||||||
|
assert real_schema == target_schema, (
|
||||||
|
_("We expected a schema of {} but instead got {}")
|
||||||
|
.format(target_schema, real_schema)
|
||||||
|
)
|
||||||
@@ -78,3 +78,13 @@ def assert_pset(element, pset_name, prop_name=None, value=None):
|
|||||||
assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format(
|
assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format(
|
||||||
value, actual_value, element
|
value, actual_value, element
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def switch_locale(locale_dir, locale_id="en"):
|
||||||
|
from gettext import translation
|
||||||
|
newlang = translation(
|
||||||
|
"messages",
|
||||||
|
localedir=locale_dir,
|
||||||
|
languages=[locale_id]
|
||||||
|
)
|
||||||
|
newlang.install()
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"tr_lang": "de",
|
||||||
|
"tr_success": "Bestanden",
|
||||||
|
"tr_failure": "Durchgefallen",
|
||||||
|
"tr_tests_passed": "Erfolgreiche Tests",
|
||||||
|
"tr_duration": "Dauer",
|
||||||
|
"tr_auditing": "OpenBIM auditing ist eine Funktionalität von",
|
||||||
|
"tr_and": "und"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"tr_lang": "en",
|
||||||
|
"tr_success": "Success",
|
||||||
|
"tr_failure": "Failure",
|
||||||
|
"tr_tests_passed": "Tests passed",
|
||||||
|
"tr_duration": "Duration",
|
||||||
|
"tr_auditing": "OpenBIM auditing is a feature of",
|
||||||
|
"tr_and": "and"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"tr_lang": "fr",
|
||||||
|
"tr_success": "Succès",
|
||||||
|
"tr_failure": "Échec",
|
||||||
|
"tr_tests_passed": "Tests réussis",
|
||||||
|
"tr_duration": "Durée",
|
||||||
|
"tr_auditing": "L'audit OpenBIM auditing est une fonctionnalité de",
|
||||||
|
"tr_and": "et"
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang={{tr_lang}}>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="foobaro">
|
<meta name="description" content="foobaro">
|
||||||
<title>BlenderBIM</title>
|
<title>{{name}}</title>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Comfortaa|Inconsolata|Open+Sans&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Comfortaa|Inconsolata|Open+Sans&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
body { font-family: 'Arial', sans-serif; padding: 40px; }
|
body { font-family: 'Arial', sans-serif; padding: 40px; }
|
||||||
@@ -30,8 +30,8 @@
|
|||||||
<h1>{{name}}</h1>
|
<h1>{{name}}</h1>
|
||||||
<p><strong>{{time}} {{file_name}}</strong></p>
|
<p><strong>{{time}} {{file_name}}</strong></p>
|
||||||
<hr>
|
<hr>
|
||||||
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}Success{{/is_success}}{{^is_success}}Failure{{/is_success}}</span>
|
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}{{tr_success}}{{/is_success}}{{^is_success}}{{tr_failure}}{{/is_success}}</span>
|
||||||
Tests passed: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
{{tr_tests_passed}}: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
||||||
<br />
|
<br />
|
||||||
<p class="description">
|
<p class="description">
|
||||||
{{#description}}
|
{{#description}}
|
||||||
@@ -44,10 +44,10 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>{{name}}</h2>
|
<h2>{{name}}</h2>
|
||||||
<p>
|
<p>
|
||||||
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}Success{{/is_success}}{{^is_success}}Failure{{/is_success}}</span>
|
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}{{tr_success}}{{/is_success}}{{^is_success}}{{tr_failure}}{{/is_success}}</span>
|
||||||
Tests passed: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
{{tr_tests_passed}}: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
||||||
<span class="time">
|
<span class="time">
|
||||||
Duration: {{time}}s
|
{{tr_duration}}: {{time}}s
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<ol>
|
<ol>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<footer>
|
<footer>
|
||||||
<p>
|
<p>
|
||||||
OpenBIM auditing is a feature of <a href="https://blenderbim.org/">BlenderBIM</a> and <a href="http://ifcopenshell.org/">IfcOpenShell</a>.
|
{{tr_auditing}} <a href="https://blenderbim.org/">BlenderBIM</a> {{tr_and}} <a href="http://ifcopenshell.org/">IfcOpenShell</a>.
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
# German translations for PROJECT.
|
||||||
|
# Copyright (C) 2020 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2020-12-17 15:14+0100\n"
|
||||||
|
"PO-Revision-Date: 2020-12-18 06:11+0100\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: de\n"
|
||||||
|
"Language-Team: de <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:7
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
# German translations for PROJECT.
|
||||||
|
# Copyright (C) 2020 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2020-12-17 15:14+0100\n"
|
||||||
|
"PO-Revision-Date: 2020-12-18 06:12+0100\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: en\n"
|
||||||
|
"Language-Team: en <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:7
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr "We expected a schema of {} but instead got {}"
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
# French translations for PROJECT.
|
||||||
|
# Copyright (C) 2020 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2020-12-17 15:14+0100\n"
|
||||||
|
"PO-Revision-Date: 2020-12-18 06:12+0100\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language: fr\n"
|
||||||
|
"Language-Team: fr <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:7
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr ""
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Translations template for PROJECT.
|
||||||
|
# Copyright (C) 2020 ORGANIZATION
|
||||||
|
# This file is distributed under the same license as the PROJECT project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2020-12-17 15:14+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.6.0\n"
|
||||||
|
|
||||||
|
#: features/steps/ifcdata_methods.py:7
|
||||||
|
msgid "We expected a schema of {} but instead got {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
@@ -7,10 +7,10 @@ import pystache
|
|||||||
def generate_report(adir="."):
|
def generate_report(adir="."):
|
||||||
print("# Generating HTML reports now.")
|
print("# Generating HTML reports now.")
|
||||||
|
|
||||||
# get html template
|
# get html template path
|
||||||
html_template_file = os.path.join(
|
report_template_path = os.path.join(
|
||||||
os.path.dirname(os.path.realpath(__file__)),
|
os.path.dirname(os.path.realpath(__file__)),
|
||||||
"features/template.html"
|
"features/"
|
||||||
)
|
)
|
||||||
|
|
||||||
# get report file
|
# get report file
|
||||||
@@ -93,7 +93,40 @@ def generate_report(adir="."):
|
|||||||
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
|
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
|
||||||
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
|
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
|
||||||
|
|
||||||
html_report_file = os.path.join(report_dir, "{}.html".format(file_name))
|
# translate report
|
||||||
with open(html_report_file, "w") as out:
|
# json.dump(mydict, myfile, indent=4)
|
||||||
with open(html_template_file) as template:
|
# workaround for retrieving the feature file language
|
||||||
|
print(feature["keyword"])
|
||||||
|
if feature["keyword"] == "Feature":
|
||||||
|
strings_file_report = os.path.join(
|
||||||
|
report_template_path,
|
||||||
|
"strings_template_en.json"
|
||||||
|
)
|
||||||
|
elif feature["keyword"] == "Funktionalität":
|
||||||
|
strings_file_report = os.path.join(
|
||||||
|
report_template_path,
|
||||||
|
"strings_template_de.json"
|
||||||
|
)
|
||||||
|
elif feature["keyword"] == "Fonctionnalité":
|
||||||
|
strings_file_report = os.path.join(
|
||||||
|
report_template_path,
|
||||||
|
"strings_template_fr.json"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# standard English
|
||||||
|
strings_file_report = os.path.join(
|
||||||
|
report_template_path,
|
||||||
|
"strings_template_en.json"
|
||||||
|
)
|
||||||
|
strings_report = json.loads(
|
||||||
|
open(strings_file_report, encoding="utf8").read()
|
||||||
|
)
|
||||||
|
# print(strings_report)
|
||||||
|
data.update(strings_report)
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
html_report = os.path.join(report_dir, "{}.html".format(file_name))
|
||||||
|
html_tmpl = os.path.join(report_template_path, "template.html")
|
||||||
|
with open(html_report, "w", encoding="utf8") as out:
|
||||||
|
with open(html_tmpl, encoding="utf8") as template:
|
||||||
out.write(pystache.render(template.read(), data))
|
out.write(pystache.render(template.read(), data))
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ from behave.__main__ import main as behave_main
|
|||||||
# get bimtester source code module path
|
# get bimtester source code module path
|
||||||
bimtester_path = os.path.dirname(os.path.realpath(__file__))
|
bimtester_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
# print(bimtester_path)
|
# print(bimtester_path)
|
||||||
|
locale_path = os.path.join(bimtester_path, "locale")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
||||||
base_path = sys._MEIPASS
|
base_path = sys._MEIPASS
|
||||||
@@ -30,7 +33,16 @@ def run_tests(args):
|
|||||||
if args["advanced_arguments"]:
|
if args["advanced_arguments"]:
|
||||||
behave_args.extend(args["advanced_arguments"].split())
|
behave_args.extend(args["advanced_arguments"].split())
|
||||||
elif not args["console"]:
|
elif not args["console"]:
|
||||||
behave_args.extend(["--format", "json.pretty", "--outfile", "report/report.json"])
|
behave_args.extend([
|
||||||
|
"--format",
|
||||||
|
"json.pretty",
|
||||||
|
"--outfile",
|
||||||
|
"report/report.json"
|
||||||
|
])
|
||||||
|
behave_args.extend([
|
||||||
|
"--define",
|
||||||
|
"localedir={}".format(locale_path)
|
||||||
|
])
|
||||||
behave_main(behave_args)
|
behave_main(behave_args)
|
||||||
print("# All tests are finished.")
|
print("# All tests are finished.")
|
||||||
return True
|
return True
|
||||||
@@ -43,7 +55,13 @@ def get_features(args):
|
|||||||
if f.endswith(".feature"):
|
if f.endswith(".feature"):
|
||||||
os.remove(os.path.join(features_dir, f))
|
os.remove(os.path.join(features_dir, f))
|
||||||
if args["feature"]:
|
if args["feature"]:
|
||||||
shutil.copyfile(args["feature"], os.path.join(get_resource_path("features"), os.path.basename(args["feature"])))
|
shutil.copyfile(
|
||||||
|
args["feature"],
|
||||||
|
os.path.join(
|
||||||
|
get_resource_path("features"),
|
||||||
|
os.path.basename(args["feature"])
|
||||||
|
)
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
if os.path.exists("features"):
|
if os.path.exists("features"):
|
||||||
shutil.copytree("features", get_resource_path("features"))
|
shutil.copytree("features", get_resource_path("features"))
|
||||||
@@ -55,7 +73,10 @@ def get_features(args):
|
|||||||
if args["feature"] and args["feature"] != f:
|
if args["feature"] and args["feature"] != f:
|
||||||
continue
|
continue
|
||||||
has_features = True
|
has_features = True
|
||||||
shutil.copyfile(f, os.path.join(get_resource_path("features"), os.path.basename(f)))
|
shutil.copyfile(
|
||||||
|
f,
|
||||||
|
os.path.join(get_resource_path("features"), os.path.basename(f))
|
||||||
|
)
|
||||||
return has_features
|
return has_features
|
||||||
|
|
||||||
|
|
||||||
@@ -111,16 +132,23 @@ def run_intmp_tests(args={}):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from behave import __version__ as behave_version
|
from behave import __version__ as behave_version
|
||||||
|
|
||||||
# https://github.com/behave/behave/issues/871
|
# https://github.com/behave/behave/issues/871
|
||||||
if behave_version == "1.2.5":
|
if behave_version == "1.2.5":
|
||||||
print("At least behave version 1.2.6 is needed, but version {} found.".format(behave_version))
|
print(
|
||||||
|
"At least behave version 1.2.6 is needed, but version {} found."
|
||||||
|
.format(behave_version)
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# get the features_path, the feature files where the tests are in
|
# get the features_path, the feature files where the tests are in
|
||||||
if "features" in args and args["features"] != "":
|
if "features" in args and args["features"] != "":
|
||||||
# TODO check if path exists, and if features dir is inside
|
|
||||||
features_path = os.path.join(args["features"], "features")
|
features_path = os.path.join(args["features"], "features")
|
||||||
|
if not os.path.isdir(features_path):
|
||||||
|
print(
|
||||||
|
"The features directory does not exist: {}"
|
||||||
|
.format(features_path)
|
||||||
|
)
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
# TODO assume features beside ifc thus use ifc path
|
# TODO assume features beside ifc thus use ifc path
|
||||||
print("No features path was given.")
|
print("No features path was given.")
|
||||||
@@ -152,7 +180,6 @@ def run_intmp_tests(args={}):
|
|||||||
run_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
run_path = os.path.join(tempfile.gettempdir(), "bimtesterfc")
|
||||||
if os.path.isdir(run_path):
|
if os.path.isdir(run_path):
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
|
||||||
rmtree(run_path) # fails on read only files
|
rmtree(run_path) # fails on read only files
|
||||||
if os.path.isdir(run_path):
|
if os.path.isdir(run_path):
|
||||||
print("Delete former beimtester run dir {} failed".format(run_path))
|
print("Delete former beimtester run dir {} failed".format(run_path))
|
||||||
@@ -185,7 +212,10 @@ def run_intmp_tests(args={}):
|
|||||||
theline = line
|
theline = line
|
||||||
if ifc_filename is None:
|
if ifc_filename is None:
|
||||||
ifc_filename = os.path.basename(theline.split('"')[1])
|
ifc_filename = os.path.basename(theline.split('"')[1])
|
||||||
newifcline = ' * The IFC file "{}" must be provided\n'.format(os.path.join(ifc_path, ifc_filename))
|
newifcline = (
|
||||||
|
' * The IFC file "{}" must be provided\n'
|
||||||
|
.format(os.path.join(ifc_path, ifc_filename))
|
||||||
|
)
|
||||||
# print(newifcline)
|
# print(newifcline)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -199,15 +229,26 @@ def run_intmp_tests(args={}):
|
|||||||
print(line.replace(theline, newifcline), end="")
|
print(line.replace(theline, newifcline), end="")
|
||||||
|
|
||||||
# copy step files and environment file
|
# copy step files and environment file
|
||||||
steps_path = os.path.join(bimtester_path, "features", "steps")
|
steps_path = os.path.join(
|
||||||
|
bimtester_path,
|
||||||
|
"features",
|
||||||
|
"steps"
|
||||||
|
)
|
||||||
# print(steps_path)
|
# print(steps_path)
|
||||||
# print(copy_steps_path)
|
# print(copy_steps_path)
|
||||||
if os.path.exists(steps_path):
|
if os.path.exists(steps_path):
|
||||||
shutil.copytree(steps_path, copy_steps_path)
|
shutil.copytree(steps_path, copy_steps_path)
|
||||||
|
|
||||||
environment_file = os.path.join(bimtester_path, "features", "environment.py")
|
environment_file = os.path.join(
|
||||||
|
bimtester_path,
|
||||||
|
"features",
|
||||||
|
"environment.py"
|
||||||
|
)
|
||||||
if os.path.isfile(environment_file):
|
if os.path.isfile(environment_file):
|
||||||
shutil.copyfile(environment_file, os.path.join(copy_features_path, "environment.py"))
|
shutil.copyfile(
|
||||||
|
environment_file,
|
||||||
|
os.path.join(copy_features_path, "environment.py")
|
||||||
|
)
|
||||||
|
|
||||||
# get advanced args
|
# get advanced args
|
||||||
# print to console from inside step files, add "--no-capture" flag
|
# print to console from inside step files, add "--no-capture" flag
|
||||||
@@ -216,27 +257,27 @@ def run_intmp_tests(args={}):
|
|||||||
if "advanced_arguments" in args:
|
if "advanced_arguments" in args:
|
||||||
behave_args.extend(args["advanced_arguments"].split())
|
behave_args.extend(args["advanced_arguments"].split())
|
||||||
elif "console" not in args:
|
elif "console" not in args:
|
||||||
behave_args.extend(
|
behave_args.extend([
|
||||||
[
|
# redirect prints in step methods
|
||||||
# redirect prints in step methods
|
# if step fails some output is catched, thus might not be printed
|
||||||
# if step fails some output is catched, thus might not be printed
|
"--no-capture",
|
||||||
"--no-capture",
|
# next two lines are one arg
|
||||||
# next two lines are one arg
|
"--format",
|
||||||
"--format",
|
"json.pretty",
|
||||||
"json.pretty",
|
# next two lines are one arg
|
||||||
# next two lines are one arg
|
"--outfile",
|
||||||
"--outfile",
|
os.path.join(report_path, "report.json"),
|
||||||
os.path.join(report_path, "report.json"),
|
# next two lines are one arg
|
||||||
# next two lines are one arg
|
"--define",
|
||||||
"--define",
|
"ifcbasename={}".format(os.path.splitext(ifc_filename)[0]),
|
||||||
"ifcbasename={}".format(os.path.splitext(ifc_filename)[0]),
|
# next two lines are one arg
|
||||||
]
|
"--define",
|
||||||
)
|
"localedir={}".format(locale_path)
|
||||||
|
])
|
||||||
print(behave_args)
|
print(behave_args)
|
||||||
|
|
||||||
# run tests
|
# run tests
|
||||||
from behave.__main__ import main as behave_main
|
from behave.__main__ import main as behave_main
|
||||||
|
|
||||||
behave_main(behave_args)
|
behave_main(behave_args)
|
||||||
print("All tests are finished.")
|
print("All tests are finished.")
|
||||||
|
|
||||||
@@ -249,7 +290,10 @@ def run_intmp_tests(args={}):
|
|||||||
def run_all(the_features_path, the_ifcfile):
|
def run_all(the_features_path, the_ifcfile):
|
||||||
|
|
||||||
# run bimtester
|
# run bimtester
|
||||||
runpath = run_intmp_tests({"features": the_features_path, "ifcfile": the_ifcfile})
|
runpath = run_intmp_tests({
|
||||||
|
"features": the_features_path,
|
||||||
|
"ifcfile": the_ifcfile
|
||||||
|
})
|
||||||
print(runpath)
|
print(runpath)
|
||||||
|
|
||||||
# check if it worked out well
|
# check if it worked out well
|
||||||
@@ -263,12 +307,17 @@ def run_all(the_features_path, the_ifcfile):
|
|||||||
|
|
||||||
# create html report and open in webbrowser
|
# create html report and open in webbrowser
|
||||||
from .reports import generate_report
|
from .reports import generate_report
|
||||||
|
|
||||||
generate_report(runpath)
|
generate_report(runpath)
|
||||||
# get the feature files
|
# get the feature files
|
||||||
feature_files = os.listdir(os.path.join(the_features_path, "features"))
|
feature_files = os.listdir(
|
||||||
|
os.path.join(the_features_path, "features")
|
||||||
|
)
|
||||||
# print(feature_files)
|
# print(feature_files)
|
||||||
for ff in feature_files:
|
for ff in feature_files:
|
||||||
webbrowser.open(os.path.join(runpath, "report", ff + ".html"))
|
webbrowser.open(os.path.join(
|
||||||
|
runpath,
|
||||||
|
"report",
|
||||||
|
ff + ".html"
|
||||||
|
))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -138,57 +138,22 @@ endif
|
|||||||
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
|
cp -r dist/working/pyparsing-2.4.5/pyparsing.py dist/blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by bcfplugin
|
# Required by bcf
|
||||||
mkdir dist/working
|
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz
|
|
||||||
cd dist/working && tar -xzvf pytz*
|
|
||||||
cp -r dist/working/pytz-2019.3/pytz dist/blenderbim/libs/site/packages/
|
|
||||||
rm -rf dist/working
|
|
||||||
|
|
||||||
# Required by bcfplugin
|
|
||||||
mkdir dist/working
|
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz
|
|
||||||
cd dist/working && tar -xzvf python-dateutil*
|
|
||||||
cp -r dist/working/python-dateutil-2.8.1/dateutil dist/blenderbim/libs/site/packages/
|
|
||||||
rm -rf dist/working
|
|
||||||
|
|
||||||
# Required by bcfplugin
|
|
||||||
mkdir dist/working
|
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/21/9f/b251f7f8a76dec1d6651be194dfba8fb8d7781d10ab3987190de8391d08e/six-1.14.0.tar.gz
|
|
||||||
cd dist/working && tar -xzvf six*
|
|
||||||
cp -r dist/working/six-1.14.0/six.py dist/blenderbim/libs/site/packages/
|
|
||||||
rm -rf dist/working
|
|
||||||
|
|
||||||
# Required by bcfplugin
|
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/bb/41/ad9ce53bb978b68af8ae415293cafc89b165b8ad55a593725299dca76729/xmlschema-1.1.1.tar.gz
|
cd dist/working && wget https://files.pythonhosted.org/packages/bb/41/ad9ce53bb978b68af8ae415293cafc89b165b8ad55a593725299dca76729/xmlschema-1.1.1.tar.gz
|
||||||
cd dist/working && tar -xzvf xmlschema*
|
cd dist/working && tar -xzvf xmlschema*
|
||||||
cp -r dist/working/xmlschema-1.1.1/xmlschema dist/blenderbim/libs/site/packages/
|
cp -r dist/working/xmlschema-1.1.1/xmlschema dist/blenderbim/libs/site/packages/
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by bcfplugin
|
# Provides bcf functionality
|
||||||
mkdir dist/working
|
mkdir dist/blenderbim/libs/site/packages/bcf
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/12/f9/f9960222d5274944b01391749e55e4dcdf28d8f0c108b64ac931ceff6fdb/elementpath-1.4.3.tar.gz
|
mkdir dist/blenderbim/libs/site/packages/bcf/xsd
|
||||||
cd dist/working && tar -xzvf elementpath*
|
cd dist/blenderbim/libs/site/packages/bcf && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/bcfxml.py
|
||||||
cp -r dist/working/elementpath-1.4.3/elementpath dist/blenderbim/libs/site/packages/
|
cd dist/blenderbim/libs/site/packages/bcf && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/data.py
|
||||||
rm -rf dist/working
|
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/markup.xsd
|
||||||
|
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/project.xsd
|
||||||
# Provides bcfplugin functionality
|
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/version.xsd
|
||||||
mkdir dist/working
|
cd dist/blenderbim/libs/site/packages/bcf/xsd && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/bcf/bcf/xsd/visinfo.xsd
|
||||||
cd dist/working && wget https://github.com/podestplatz/bcf/archive/master.zip
|
|
||||||
cd dist/working && unzip master*
|
|
||||||
cp -r dist/working/bcf-master/bcfplugin dist/blenderbim/libs/site/packages/
|
|
||||||
rm -rf dist/working
|
|
||||||
|
|
||||||
# Required by bcfplugin
|
|
||||||
mkdir dist/working
|
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/buildingSMART/BCF-XML/release_2_1/Schemas/project.xsd
|
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/buildingSMART/BCF-XML/release_2_1/Extension%20Schemas/extensions.xsd
|
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/buildingSMART/BCF-XML/release_2_1/Schemas/markup.xsd
|
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/buildingSMART/BCF-XML/release_2_1/Schemas/version.xsd
|
|
||||||
cd dist/working && wget https://raw.githubusercontent.com/buildingSMART/BCF-XML/release_2_1/Schemas/visinfo.xsd
|
|
||||||
mkdir dist/blenderbim/libs/site/packages/bcfplugin/schemas/
|
|
||||||
cp -r dist/working/*.xsd dist/blenderbim/libs/site/packages/bcfplugin/schemas/
|
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by IFCCSV and ifcopenshell.util.selector
|
# Required by IFCCSV and ifcopenshell.util.selector
|
||||||
|
|||||||
@@ -25,15 +25,16 @@ if bpy is not None:
|
|||||||
operator.UnassignClass,
|
operator.UnassignClass,
|
||||||
operator.SelectClass,
|
operator.SelectClass,
|
||||||
operator.SelectType,
|
operator.SelectType,
|
||||||
operator.SelectBcfFile,
|
operator.NewBcfProject,
|
||||||
operator.GetBcfTopics,
|
operator.LoadBcfProject,
|
||||||
|
operator.LoadBcfTopics,
|
||||||
|
operator.LoadBcfComments,
|
||||||
|
operator.SaveBcfProject,
|
||||||
|
operator.AddBcfTopic,
|
||||||
operator.ViewBcfTopic,
|
operator.ViewBcfTopic,
|
||||||
operator.ActivateBcfViewpoint,
|
operator.ActivateBcfViewpoint,
|
||||||
operator.OpenBcfFileReference,
|
operator.OpenUri,
|
||||||
operator.OpenBcfReferenceLink,
|
operator.OpenBcfReferenceLink,
|
||||||
operator.OpenBcfBimSnippetSchema,
|
|
||||||
operator.OpenBcfBimSnippetReference,
|
|
||||||
operator.OpenBcfDocumentReference,
|
|
||||||
operator.SelectFeaturesDir,
|
operator.SelectFeaturesDir,
|
||||||
operator.SelectDiffJsonFile,
|
operator.SelectDiffJsonFile,
|
||||||
operator.SelectDiffNewFile,
|
operator.SelectDiffNewFile,
|
||||||
@@ -258,12 +259,10 @@ if bpy is not None:
|
|||||||
prop.Schedule,
|
prop.Schedule,
|
||||||
prop.DrawingStyle,
|
prop.DrawingStyle,
|
||||||
prop.Sheet,
|
prop.Sheet,
|
||||||
|
prop.BcfBimSnippet,
|
||||||
|
prop.BcfDocumentReference,
|
||||||
|
prop.BcfComment,
|
||||||
prop.BcfTopic,
|
prop.BcfTopic,
|
||||||
prop.BcfTopicLabel,
|
|
||||||
prop.BcfTopicFile,
|
|
||||||
prop.BcfTopicLink,
|
|
||||||
prop.BcfTopicDocumentReference,
|
|
||||||
prop.BcfTopicRelatedTopic,
|
|
||||||
prop.Subcontext,
|
prop.Subcontext,
|
||||||
prop.PresentationLayer,
|
prop.PresentationLayer,
|
||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
@@ -298,6 +297,7 @@ if bpy is not None:
|
|||||||
ui.BIM_PT_ifccsv,
|
ui.BIM_PT_ifccsv,
|
||||||
ui.BIM_PT_ifcclash,
|
ui.BIM_PT_ifcclash,
|
||||||
ui.BIM_PT_bcf,
|
ui.BIM_PT_bcf,
|
||||||
|
ui.BIM_PT_bcf_comments,
|
||||||
ui.BIM_PT_owner,
|
ui.BIM_PT_owner,
|
||||||
ui.BIM_PT_people,
|
ui.BIM_PT_people,
|
||||||
ui.BIM_PT_organisations,
|
ui.BIM_PT_organisations,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
class BcfStore:
|
|
||||||
topics = []
|
|
||||||
viewpoints = []
|
|
||||||
comments = []
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import bcf
|
||||||
|
import bcf.bcfxml
|
||||||
|
|
||||||
|
class BcfStore:
|
||||||
|
bcfxml = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_bcfxml():
|
||||||
|
if not BcfStore.bcfxml:
|
||||||
|
BcfStore.bcfxml = bcf.bcfxml.BcfXml()
|
||||||
|
return BcfStore.bcfxml
|
||||||
@@ -482,13 +482,20 @@ class IfcCutter:
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
multiprocessing.set_executable(bpy.app.binary_path_python)
|
if bpy.app.version > (2, 90, 0) and os.name == 'nt':
|
||||||
|
# See bug #1148
|
||||||
with multiprocessing.Pool(9) as p:
|
for data in process_data:
|
||||||
results = p.map(do_cut, process_data)
|
results = do_cut(data)
|
||||||
for result in results:
|
polygons = [r for r in results if r["points"]]
|
||||||
polygons = [p for p in result if p["points"]]
|
|
||||||
self.cut_polygons.extend(polygons)
|
self.cut_polygons.extend(polygons)
|
||||||
|
else:
|
||||||
|
multiprocessing.set_executable(bpy.app.binary_path_python)
|
||||||
|
|
||||||
|
with multiprocessing.Pool(9) as p:
|
||||||
|
results = p.map(do_cut, process_data)
|
||||||
|
for result in results:
|
||||||
|
polygons = [p for p in result if p["points"]]
|
||||||
|
self.cut_polygons.extend(polygons)
|
||||||
|
|
||||||
def get_polygon_metadata(self, polygon, position):
|
def get_polygon_metadata(self, polygon, position):
|
||||||
polygon["metadata"] = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), position)}
|
polygon["metadata"] = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), position)}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"""Viewport decorations"""
|
"""Viewport decorations"""
|
||||||
import math
|
import math
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from bpy.types import SpaceView3D
|
from bpy.types import SpaceView3D
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
import bpy
|
import bpy
|
||||||
@@ -70,6 +72,7 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
// converting to and from square-space coordinates to calculate arrows
|
// converting to and from square-space coordinates to calculate arrows
|
||||||
mat4 clip2square = mat4(aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
mat4 clip2square = mat4(aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
mat4 square2clip = mat4(1/aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
mat4 square2clip = mat4(1/aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
|
|
||||||
vec4 dir = normalize((p1 - p0) * clip2square) * length;
|
vec4 dir = normalize((p1 - p0) * clip2square) * length;
|
||||||
vec4 head = dir * square2clip;
|
vec4 head = dir * square2clip;
|
||||||
vec4 arr_a = dir * rot_a * square2clip;
|
vec4 arr_a = dir * rot_a * square2clip;
|
||||||
@@ -136,14 +139,23 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
drawing = self.props.drawings[self.props.active_drawing_index]
|
drawing = self.props.drawings[self.props.active_drawing_index]
|
||||||
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
||||||
|
|
||||||
curves = [o for o in collection.objects if "IfcAnnotation/Dimension" in o.name]
|
segments = self.get_segments(self.get_curves(collection, "IfcAnnotation/Dimension"))
|
||||||
segments = []
|
|
||||||
for curve in curves:
|
|
||||||
segments.extend(list(self.iter_segments(curve)))
|
|
||||||
|
|
||||||
self.draw_arrows(segments)
|
self.draw_arrows(segments)
|
||||||
for segm in segments:
|
for segm in segments:
|
||||||
self.draw_label(segm)
|
self.draw_label(segm, f"{segm[2]:.2f}")
|
||||||
|
|
||||||
|
segments = self.get_segments(self.get_curves(collection, "IfcAnnotation/Equal"))
|
||||||
|
|
||||||
|
self.draw_arrows(segments)
|
||||||
|
for segm in segments:
|
||||||
|
self.draw_label(segm, "EQ")
|
||||||
|
|
||||||
|
def get_curves(self, collection, basename):
|
||||||
|
return list(filter(lambda o: basename in o.name, collection.objects))
|
||||||
|
|
||||||
|
def get_segments(self, curves):
|
||||||
|
return list(chain.from_iterable(self.iter_segments(curve) for curve in curves))
|
||||||
|
|
||||||
def iter_segments(self, curve):
|
def iter_segments(self, curve):
|
||||||
"""Yields each segment converted to world coords
|
"""Yields each segment converted to world coords
|
||||||
@@ -158,21 +170,23 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
length = (p1 - p0).length
|
length = (p1 - p0).length
|
||||||
yield (p0, p1, length)
|
yield (p0, p1, length)
|
||||||
|
|
||||||
def draw_label(self, segm):
|
def draw_label(self, segm, text):
|
||||||
"""Draw text of segment length
|
"""Draw text of segment length
|
||||||
aligned and centered at segment middle
|
aligned and centered at segment middle
|
||||||
"""
|
"""
|
||||||
p0, p1, length = segm
|
p0, p1, _ = segm
|
||||||
|
|
||||||
# convert to view coords
|
# convert to view coords
|
||||||
region = self.context.region
|
region = self.context.region
|
||||||
region3d = self.context.region_data
|
region3d = self.context.region_data
|
||||||
p0 = location_3d_to_region_2d(region, region3d, p0)
|
p0 = location_3d_to_region_2d(region, region3d, p0)
|
||||||
p1 = location_3d_to_region_2d(region, region3d, p1)
|
p1 = location_3d_to_region_2d(region, region3d, p1)
|
||||||
|
proj = p1 - p0
|
||||||
|
|
||||||
text = f"{length:.2f}"
|
if proj.length < 0.001:
|
||||||
|
return
|
||||||
|
|
||||||
ang = -Vector((1, 0)).angle_signed(p1 - p0)
|
ang = -Vector((1, 0)).angle_signed(proj)
|
||||||
cos = math.cos(ang)
|
cos = math.cos(ang)
|
||||||
sin = math.sin(ang)
|
sin = math.sin(ang)
|
||||||
|
|
||||||
@@ -208,12 +222,20 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
||||||
self.shader.bind()
|
self.shader.bind()
|
||||||
|
|
||||||
matrix = self.context.region_data.perspective_matrix
|
region = self.context.region_data
|
||||||
|
matrix = region.perspective_matrix
|
||||||
aspect = self.context.region.width / self.context.region.height
|
aspect = self.context.region.width / self.context.region.height
|
||||||
self.shader.uniform_float("viewMatrix", matrix)
|
self.shader.uniform_float("viewMatrix", matrix)
|
||||||
# TODO: get everything from styles
|
# TODO: get everything from styles
|
||||||
self.shader.uniform_float('aspect', aspect)
|
self.shader.uniform_float('aspect', aspect)
|
||||||
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
||||||
self.shader.uniform_float('angle', math.pi / 12)
|
self.shader.uniform_float('angle', math.pi / 12)
|
||||||
self.shader.uniform_float('length', 32 / self.context.region.height)
|
|
||||||
|
# brute-force perspective fix
|
||||||
|
# TODO: move the fix into shader + align heads to view plane
|
||||||
|
length = 32 / self.context.region.height
|
||||||
|
if region.is_perspective:
|
||||||
|
length *= 8
|
||||||
|
|
||||||
|
self.shader.uniform_float('length', length)
|
||||||
batch.draw(self.shader)
|
batch.draw(self.shader)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from . import svgwriter
|
|||||||
from . import sheeter
|
from . import sheeter
|
||||||
from . import scheduler
|
from . import scheduler
|
||||||
from . import schema
|
from . import schema
|
||||||
from . import bcf
|
from . import bcfstore
|
||||||
from . import ifc
|
from . import ifc
|
||||||
from . import annotation
|
from . import annotation
|
||||||
from . import helper
|
from . import helper
|
||||||
@@ -507,20 +507,151 @@ class RejectElement(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class GetBcfTopics(bpy.types.Operator):
|
class NewBcfProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.get_bcf_topics"
|
bl_idname = "bim.new_bcf_project"
|
||||||
bl_label = "Get BCF Topics"
|
bl_label = "New BCF Project"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import bcfplugin
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.new_project()
|
||||||
|
bpy.ops.bim.load_bcf_project()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
bcfplugin.openProject(bpy.context.scene.BCFProperties.bcf_file)
|
|
||||||
bcf.BcfStore.topics = bcfplugin.getTopics()
|
class LoadBcfProject(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_bcf_project"
|
||||||
|
bl_label = "Load BCF Project"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
if self.filepath:
|
||||||
|
bcfxml.get_project(self.filepath)
|
||||||
|
bpy.context.scene.BCFProperties.is_editable = False
|
||||||
|
bpy.context.scene.BCFProperties.name = bcfxml.project.name
|
||||||
|
bpy.ops.bim.load_bcf_topics()
|
||||||
|
bpy.context.scene.BCFProperties.is_loaded = True
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class LoadBcfTopics(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_bcf_topics"
|
||||||
|
bl_label = "Load BCF Topics"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.get_topics()
|
||||||
while len(bpy.context.scene.BCFProperties.topics) > 0:
|
while len(bpy.context.scene.BCFProperties.topics) > 0:
|
||||||
bpy.context.scene.BCFProperties.topics.remove(0)
|
bpy.context.scene.BCFProperties.topics.remove(0)
|
||||||
for topic in bcf.BcfStore.topics:
|
for topic in bcfxml.topics.values():
|
||||||
new = bpy.context.scene.BCFProperties.topics.add()
|
new = bpy.context.scene.BCFProperties.topics.add()
|
||||||
new.name = topic[0]
|
data_map = {
|
||||||
|
"name": topic.guid,
|
||||||
|
"title": topic.title,
|
||||||
|
"type": topic.topic_type,
|
||||||
|
"status": topic.topic_status,
|
||||||
|
"priority": topic.priority,
|
||||||
|
"stage": topic.stage,
|
||||||
|
"creation_date": topic.creation_date,
|
||||||
|
"creation_author": topic.creation_author,
|
||||||
|
"modified_date": topic.modified_date,
|
||||||
|
"modified_author": topic.modified_author,
|
||||||
|
"assigned_to": topic.assigned_to,
|
||||||
|
"due_date": topic.due_date,
|
||||||
|
"description": topic.description
|
||||||
|
}
|
||||||
|
for key, value in data_map.items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(new, key, str(value))
|
||||||
|
for reference_link in topic.reference_links:
|
||||||
|
new2 = new.reference_links.add()
|
||||||
|
new2.name = reference_link
|
||||||
|
for label in topic.labels:
|
||||||
|
new2 = new.labels.add()
|
||||||
|
new2.name = label
|
||||||
|
if topic.bim_snippet:
|
||||||
|
data_map = {
|
||||||
|
"type": topic.bim_snippet.snippet_type,
|
||||||
|
"is_external": topic.bim_snippet.is_external,
|
||||||
|
"reference": topic.bim_snippet.reference,
|
||||||
|
"schema": topic.bim_snippet.reference_schema
|
||||||
|
}
|
||||||
|
for key, value in data_map.items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(new.bim_snippet, key, value)
|
||||||
|
for doc in topic.document_references:
|
||||||
|
new2 = new.document_references.add()
|
||||||
|
data_map = {
|
||||||
|
"reference": doc.referenced_document,
|
||||||
|
"description": doc.description,
|
||||||
|
"guid": doc.guid,
|
||||||
|
"is_external": doc.is_external
|
||||||
|
}
|
||||||
|
for key, value in data_map.items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(new2, key, value)
|
||||||
|
for related_topic in topic.related_topics:
|
||||||
|
new2 = new.related_topics.add()
|
||||||
|
new2.name = related_topic.guid
|
||||||
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class LoadBcfComments(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_bcf_comments"
|
||||||
|
bl_label = "Load BCF Comments"
|
||||||
|
topic_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.get_comments(self.topic_guid)
|
||||||
|
blender_topic = bpy.context.scene.BCFProperties.topics.get(self.topic_guid)
|
||||||
|
for comment in bcfxml.topics[self.topic_guid].comments.values():
|
||||||
|
new = blender_topic.comments.add()
|
||||||
|
data_map = {
|
||||||
|
"name": comment.guid,
|
||||||
|
"comment": comment.comment,
|
||||||
|
"viewpoint": comment.viewpoint.guid if comment.viewpoint else None,
|
||||||
|
"date": comment.date,
|
||||||
|
"author": comment.author,
|
||||||
|
"modified_date": comment.modified_date,
|
||||||
|
"modified_author": comment.modified_author,
|
||||||
|
}
|
||||||
|
for key, value in data_map.items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(new, key, str(value))
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SaveBcfProject(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.save_bcf_project"
|
||||||
|
bl_label = "Save BCF Project"
|
||||||
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.save_project(self.filepath)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddBcfTopic(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_bcf_topic"
|
||||||
|
bl_label = "Add BCF Topic"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
bcfxml.add_topic()
|
||||||
|
new = bpy.context.scene.BCFProperties.topics.add()
|
||||||
|
new.name = "New Topic"
|
||||||
|
bpy.ops.bim.load_bcf_topics()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -530,8 +661,8 @@ class ViewBcfTopic(bpy.types.Operator):
|
|||||||
topic_guid: bpy.props.StringProperty()
|
topic_guid: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
for index, topic in enumerate(bcf.BcfStore.topics):
|
for index, topic in enumerate(bpy.context.scene.BCFProperties.topics):
|
||||||
if str(topic[1].xmlId) == self.topic_guid:
|
if topic.guid.lower() == self.topic_guid.lower():
|
||||||
bpy.context.scene.BCFProperties.active_topic_index = index
|
bpy.context.scene.BCFProperties.active_topic_index = index
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -541,18 +672,15 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
bl_label = "Activate BCF Viewpoint"
|
bl_label = "Activate BCF Viewpoint"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import bcfplugin
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
props = bpy.context.scene.BCFProperties
|
||||||
topics = bcf.BcfStore.topics
|
blender_topic = props.topics[props.active_topic_index]
|
||||||
if not topics:
|
topic = bcfxml.topics[blender_topic.guid]
|
||||||
|
if not topic.viewpoints:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
topic = topics[bpy.context.scene.BCFProperties.active_topic_index][1]
|
|
||||||
viewpoints = bcf.BcfStore.viewpoints
|
|
||||||
if not viewpoints:
|
|
||||||
return {"FINISHED"}
|
|
||||||
viewpoint_reference = viewpoints[int(bpy.context.scene.BCFProperties.viewpoints)][1]
|
|
||||||
viewpoint = viewpoint_reference.viewpoint
|
|
||||||
|
|
||||||
|
viewpoint_guid = blender_topic.viewpoints
|
||||||
|
viewpoint = topic.viewpoints[viewpoint_guid]
|
||||||
obj = bpy.data.objects.get("Viewpoint")
|
obj = bpy.data.objects.get("Viewpoint")
|
||||||
if not obj:
|
if not obj:
|
||||||
obj = bpy.data.objects.new("Viewpoint", bpy.data.cameras.new("Viewpoint"))
|
obj = bpy.data.objects.new("Viewpoint", bpy.data.cameras.new("Viewpoint"))
|
||||||
@@ -563,13 +691,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
cam_height = bpy.context.scene.render.resolution_y
|
cam_height = bpy.context.scene.render.resolution_y
|
||||||
cam_aspect = cam_width / cam_height
|
cam_aspect = cam_width / cam_height
|
||||||
|
|
||||||
if viewpoint_reference.snapshot:
|
if viewpoint.snapshot:
|
||||||
obj.data.show_background_images = True
|
obj.data.show_background_images = True
|
||||||
while len(obj.data.background_images) > 0:
|
while len(obj.data.background_images) > 0:
|
||||||
obj.data.background_images.remove(obj.data.background_images[0])
|
obj.data.background_images.remove(obj.data.background_images[0])
|
||||||
background = obj.data.background_images.new()
|
background = obj.data.background_images.new()
|
||||||
background.image = bpy.data.images.load(
|
background.image = bpy.data.images.load(
|
||||||
os.path.join(bcfplugin.util.getBcfDir(), str(topic.xmlId), viewpoint_reference.snapshot.uri)
|
os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot)
|
||||||
)
|
)
|
||||||
src_width = background.image.size[0]
|
src_width = background.image.size[0]
|
||||||
src_height = background.image.size[1]
|
src_height = background.image.size[1]
|
||||||
@@ -583,18 +711,18 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||||
area.spaces[0].region_3d.view_perspective = "CAMERA"
|
area.spaces[0].region_3d.view_perspective = "CAMERA"
|
||||||
|
|
||||||
if viewpoint.oCamera:
|
if viewpoint.orthogonal_camera:
|
||||||
camera = viewpoint.oCamera
|
camera = viewpoint.orthogonal_camera
|
||||||
obj.data.type = "ORTHO"
|
obj.data.type = "ORTHO"
|
||||||
obj.data.ortho_scale = viewpoint.oCamera.viewWorldScale
|
obj.data.ortho_scale = viewpoint.orthogonal_camera.view_to_world_scale
|
||||||
elif viewpoint.pCamera:
|
elif viewpoint.perspective_camera:
|
||||||
camera = viewpoint.pCamera
|
camera = viewpoint.perspective_camera
|
||||||
obj.data.type = "PERSP"
|
obj.data.type = "PERSP"
|
||||||
if cam_aspect >= 1:
|
if cam_aspect >= 1:
|
||||||
obj.data.angle = radians(camera.fieldOfView)
|
obj.data.angle = radians(camera.field_of_view)
|
||||||
else:
|
else:
|
||||||
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
|
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
|
||||||
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.fieldOfView) / 2)))
|
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2)))
|
||||||
|
|
||||||
self.set_viewpoint_components(viewpoint)
|
self.set_viewpoint_components(viewpoint)
|
||||||
|
|
||||||
@@ -605,37 +733,37 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
self.draw_lines(viewpoint)
|
self.draw_lines(viewpoint)
|
||||||
|
|
||||||
self.delete_clipping_planes()
|
self.delete_clipping_planes()
|
||||||
if viewpoint.clippingPlanes:
|
if viewpoint.clipping_planes:
|
||||||
self.create_clipping_planes(viewpoint)
|
self.create_clipping_planes(viewpoint)
|
||||||
|
|
||||||
self.delete_bitmaps()
|
self.delete_bitmaps()
|
||||||
if viewpoint.bitmaps:
|
if viewpoint.bitmaps:
|
||||||
self.create_bitmaps(viewpoint)
|
self.create_bitmaps(bcfxml, viewpoint, topic)
|
||||||
|
|
||||||
z_axis = Vector((-camera.direction.x, -camera.direction.y, -camera.direction.z)).normalized()
|
z_axis = Vector((-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)).normalized()
|
||||||
y_axis = Vector((camera.upVector.x, camera.upVector.y, camera.upVector.z)).normalized()
|
y_axis = Vector((camera.camera_up_vector.x, camera.camera_up_vector.y, camera.camera_up_vector.z)).normalized()
|
||||||
x_axis = y_axis.cross(z_axis).normalized()
|
x_axis = y_axis.cross(z_axis).normalized()
|
||||||
rotation = Matrix((x_axis, y_axis, z_axis))
|
rotation = Matrix((x_axis, y_axis, z_axis))
|
||||||
rotation.invert()
|
rotation.invert()
|
||||||
location = Vector((camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z))
|
location = Vector((camera.camera_view_point.x, camera.camera_view_point.y, camera.camera_view_point.z))
|
||||||
obj.matrix_world = rotation.to_4x4()
|
obj.matrix_world = rotation.to_4x4()
|
||||||
obj.location = location
|
obj.location = location
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def set_viewpoint_components(self, viewpoint):
|
def set_viewpoint_components(self, viewpoint):
|
||||||
selected_global_ids = [s.ifcId for s in viewpoint.components.selection]
|
selected_global_ids = [s.ifc_guid for s in viewpoint.components.selection]
|
||||||
exception_global_ids = [v.ifcId for v in viewpoint.components.visibilityExceptions]
|
exception_global_ids = [v.ifc_guid for v in viewpoint.components.visibility.exceptions]
|
||||||
global_id_colours = {}
|
global_id_colours = {}
|
||||||
for colouring in viewpoint.components.colouring:
|
for coloring in viewpoint.components.coloring:
|
||||||
for component in colouring.components:
|
for component in coloring.components:
|
||||||
global_id_colours.setdefault(component.ifcId, colouring.colour)
|
global_id_colours.setdefault(component.ifc_guid, coloring.color)
|
||||||
|
|
||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
|
||||||
if not global_id:
|
if not global_id:
|
||||||
continue
|
continue
|
||||||
global_id = global_id.string_value
|
global_id = global_id.string_value
|
||||||
is_visible = viewpoint.components.visibilityDefault
|
is_visible = viewpoint.components.visibility.default_visibility
|
||||||
if global_id in exception_global_ids:
|
if global_id in exception_global_ids:
|
||||||
is_visible = not is_visible
|
is_visible = not is_visible
|
||||||
if not is_visible:
|
if not is_visible:
|
||||||
@@ -666,12 +794,12 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
stroke.points.add(len(viewpoint.lines) * 2)
|
stroke.points.add(len(viewpoint.lines) * 2)
|
||||||
coords = []
|
coords = []
|
||||||
for l in viewpoint.lines:
|
for l in viewpoint.lines:
|
||||||
coords.extend([l.start.x, l.start.y, l.start.z, l.end.x, l.end.y, l.end.z])
|
coords.extend([l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z])
|
||||||
stroke.points.foreach_set("co", coords)
|
stroke.points.foreach_set("co", coords)
|
||||||
|
|
||||||
def create_clipping_planes(self, viewpoint):
|
def create_clipping_planes(self, viewpoint):
|
||||||
n = 0
|
n = 0
|
||||||
for plane in viewpoint.clippingPlanes:
|
for plane in viewpoint.clipping_planes:
|
||||||
bpy.ops.bim.add_section_plane()
|
bpy.ops.bim.add_section_plane()
|
||||||
if n == 0:
|
if n == 0:
|
||||||
obj = bpy.data.objects["Section"]
|
obj = bpy.data.objects["Section"]
|
||||||
@@ -700,18 +828,14 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
for bitmap in collection.objects:
|
for bitmap in collection.objects:
|
||||||
bpy.data.objects.remove(bitmap)
|
bpy.data.objects.remove(bitmap)
|
||||||
|
|
||||||
def create_bitmaps(self, viewpoint):
|
def create_bitmaps(self, bcfxml, viewpoint, topic):
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
topics = bcf.BcfStore.topics
|
|
||||||
topic = topics[bpy.context.scene.BCFProperties.active_topic_index][1]
|
|
||||||
collection = bpy.data.collections.get("Bitmaps")
|
collection = bpy.data.collections.get("Bitmaps")
|
||||||
if not collection:
|
if not collection:
|
||||||
collection = bpy.data.collections.new("Bitmaps")
|
collection = bpy.data.collections.new("Bitmaps")
|
||||||
for bitmap in viewpoint.bitmaps:
|
for bitmap in viewpoint.bitmaps:
|
||||||
obj = bpy.data.objects.new("Bitmap", None)
|
obj = bpy.data.objects.new("Bitmap", None)
|
||||||
obj.empty_display_type = "IMAGE"
|
obj.empty_display_type = "IMAGE"
|
||||||
image = bpy.data.images.load(os.path.join(bcfplugin.util.getBcfDir(), str(topic.xmlId), bitmap.reference))
|
image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
|
||||||
src_width = image.size[0]
|
src_width = image.size[0]
|
||||||
src_height = image.size[1]
|
src_height = image.size[1]
|
||||||
if src_height > src_width:
|
if src_height > src_width:
|
||||||
@@ -719,7 +843,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
obj.empty_display_size = bitmap.height * (src_width / src_height)
|
obj.empty_display_size = bitmap.height * (src_width / src_height)
|
||||||
obj.data = image
|
obj.data = image
|
||||||
y = Vector((bitmap.upVector.x, bitmap.upVector.y, bitmap.upVector.z))
|
y = Vector((bitmap.up.x, bitmap.up.y, bitmap.up.z))
|
||||||
z = Vector((bitmap.normal.x, bitmap.normal.y, bitmap.normal.z))
|
z = Vector((bitmap.normal.x, bitmap.normal.y, bitmap.normal.z))
|
||||||
x = y.cross(z)
|
x = y.cross(z)
|
||||||
obj.matrix_world = Matrix(
|
obj.matrix_world = Matrix(
|
||||||
@@ -735,22 +859,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
|||||||
return [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
|
return [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
|
||||||
|
|
||||||
|
|
||||||
class OpenBcfFileReference(bpy.types.Operator):
|
class OpenUri(bpy.types.Operator):
|
||||||
bl_idname = "bim.open_bcf_file_reference"
|
bl_idname = "bim.open_uri"
|
||||||
bl_label = "Open BCF File Reference"
|
bl_label = "Open URI"
|
||||||
data: bpy.props.StringProperty()
|
uri: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if "/" not in self.data:
|
webbrowser.open(self.uri)
|
||||||
webbrowser.open(bpy.context.scene.BCFProperties.topic_files[int(self.data)].reference)
|
|
||||||
return {"FINISHED"}
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
topic_guid, index = self.data.split("/")
|
|
||||||
path = os.path.join(bcfplugin.util.getBcfDir(), topic_guid)
|
|
||||||
# bpy.context.scene.BCFProperties.topic_files[int(index)].reference)
|
|
||||||
# TODO - maybe allow immediate importing?
|
|
||||||
webbrowser.open(path)
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -764,53 +879,6 @@ class OpenBcfReferenceLink(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class OpenBcfBimSnippetSchema(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.open_bcf_bim_snippet_schema"
|
|
||||||
bl_label = "Open BCF BIM Snippet Schema"
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
webbrowser.open(bpy.context.scene.BCFProperties.topic_snippet_schema)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class OpenBcfBimSnippetReference(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.open_bcf_bim_snippet_reference"
|
|
||||||
bl_label = "Open BCF BIM Snippet Reference"
|
|
||||||
topic_guid: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
if bpy.context.scene.BCFProperties.topic_snippet_is_external:
|
|
||||||
webbrowser.open(bpy.context.scene.BCFProperties.topic_snippet_reference)
|
|
||||||
return {"FINISHED"}
|
|
||||||
webbrowser.open(
|
|
||||||
"file://"
|
|
||||||
+ os.path.join(
|
|
||||||
bcfplugin.util.getBcfDir(), self.topic_guid, bpy.context.scene.BCFProperties.topic_snippet_reference
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class OpenBcfDocumentReference(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.open_bcf_document_reference"
|
|
||||||
bl_label = "Open BCF Document Reference"
|
|
||||||
data: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
topic_guid, index = self.data.split("/")
|
|
||||||
doc = bpy.context.scene.BCFProperties.topic_document_references[int(index)]
|
|
||||||
uri = doc.name
|
|
||||||
if doc.is_external:
|
|
||||||
webbrowser.open(uri)
|
|
||||||
return {"FINISHED"}
|
|
||||||
webbrowser.open("file://" + os.path.join(bcfplugin.util.getBcfDir(), topic_guid, uri))
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class SelectAudited(bpy.types.Operator):
|
class SelectAudited(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_audited"
|
bl_idname = "bim.select_audited"
|
||||||
bl_label = "Select Audited"
|
bl_label = "Select Audited"
|
||||||
@@ -1963,7 +2031,7 @@ class SmartClashGroup(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||||
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
||||||
new_group.number = 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 id in pair:
|
||||||
@@ -1997,7 +2065,7 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||||
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
new_group = bpy.context.scene.BIMProperties.smart_clash_groups.add()
|
||||||
new_group.number = int(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 id in pair:
|
||||||
new_global_id = new_group.global_ids.add()
|
new_global_id = new_group.global_ids.add()
|
||||||
@@ -2030,20 +2098,6 @@ class SelectSmartGroup(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class SelectBcfFile(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.select_bcf_file"
|
|
||||||
bl_label = "Select BCF File"
|
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
bpy.context.scene.BCFProperties.bcf_file = self.filepath
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
context.window_manager.fileselect_add(self)
|
|
||||||
return {"RUNNING_MODAL"}
|
|
||||||
|
|
||||||
|
|
||||||
class SelectFeaturesDir(bpy.types.Operator):
|
class SelectFeaturesDir(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_features_dir"
|
bl_idname = "bim.select_features_dir"
|
||||||
bl_label = "Select Features Directory"
|
bl_label = "Select Features Directory"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import ifcopenshell.util.pset
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import export_ifc
|
from . import export_ifc
|
||||||
from . import schema
|
from . import schema
|
||||||
from . import bcf
|
from . import bcfstore
|
||||||
from . import ifc
|
from . import ifc
|
||||||
from . import annotation
|
from . import annotation
|
||||||
from . import decoration
|
from . import decoration
|
||||||
@@ -52,7 +52,7 @@ persons_enum = []
|
|||||||
organisations_enum = []
|
organisations_enum = []
|
||||||
sheets_enum = []
|
sheets_enum = []
|
||||||
vector_styles_enum = []
|
vector_styles_enum = []
|
||||||
bcfviewpoints_enum = []
|
bcfviewpoints_enum = None
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
@@ -814,7 +814,7 @@ class PresentationLayer(PropertyGroup):
|
|||||||
layer_blocked: BoolProperty(name="LayerBlocked", default=False)
|
layer_blocked: BoolProperty(name="LayerBlocked", default=False)
|
||||||
|
|
||||||
class SmartClashGroup(PropertyGroup):
|
class SmartClashGroup(PropertyGroup):
|
||||||
number: IntProperty(name="Number")
|
number: StringProperty(name="Number")
|
||||||
global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty)
|
global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty)
|
||||||
|
|
||||||
|
|
||||||
@@ -910,187 +910,75 @@ class Constraint(PropertyGroup):
|
|||||||
user_defined_qualifier: StringProperty(name="Custom Qualifier")
|
user_defined_qualifier: StringProperty(name="Custom Qualifier")
|
||||||
|
|
||||||
|
|
||||||
class BcfTopic(PropertyGroup):
|
def getBcfViewpoints(self, context):
|
||||||
name: StringProperty(name="Name")
|
global bcfviewpoints_enum
|
||||||
|
if bcfviewpoints_enum is None:
|
||||||
|
bcfviewpoints_enum = []
|
||||||
|
props = bpy.context.scene.BCFProperties
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
topic = props.topics[props.active_topic_index]
|
||||||
|
viewpoints = bcfxml.get_viewpoints(topic.name)
|
||||||
|
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints.keys())])
|
||||||
|
return bcfviewpoints_enum
|
||||||
|
|
||||||
|
|
||||||
class BcfTopicLabel(PropertyGroup):
|
class BcfBimSnippet(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
schema: StringProperty(name="Schema")
|
||||||
|
|
||||||
|
|
||||||
class BcfTopicLink(PropertyGroup):
|
|
||||||
name: StringProperty(name="Name")
|
|
||||||
|
|
||||||
|
|
||||||
class BcfTopicFile(PropertyGroup):
|
|
||||||
name: StringProperty(name="Name")
|
|
||||||
reference: StringProperty(name="Reference")
|
reference: StringProperty(name="Reference")
|
||||||
date: StringProperty(name="Date")
|
type: StringProperty(name="Type")
|
||||||
is_external: BoolProperty(name="Is External")
|
is_external: BoolProperty(name="Is External")
|
||||||
ifc_project: StringProperty(name="IFC Project")
|
|
||||||
ifc_spatial: StringProperty(name="IFC Spatial")
|
|
||||||
|
|
||||||
|
|
||||||
class BcfTopicDocumentReference(PropertyGroup):
|
class BcfDocumentReference(PropertyGroup):
|
||||||
name: StringProperty(name="Reference")
|
reference: StringProperty(name="Reference")
|
||||||
description: StringProperty(name="Description")
|
description: StringProperty(name="Description")
|
||||||
guid: StringProperty(name="GUID")
|
guid: StringProperty(name="GUID")
|
||||||
is_external: BoolProperty(name="Is External")
|
is_external: BoolProperty(name="Is External")
|
||||||
|
|
||||||
|
|
||||||
class BcfTopicRelatedTopic(PropertyGroup):
|
class BcfComment(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="GUID")
|
||||||
guid: StringProperty(name="GUID")
|
date: StringProperty(name="Date")
|
||||||
|
author: StringProperty(name="Author")
|
||||||
|
comment: StringProperty(name="Comment")
|
||||||
|
viewpoint: StringProperty(name="Viewpoint")
|
||||||
|
modified_date: StringProperty(name="Modified Date")
|
||||||
|
modified_author: StringProperty(name="Modified Author")
|
||||||
|
|
||||||
|
|
||||||
|
class BcfTopic(PropertyGroup):
|
||||||
|
name: StringProperty(name="GUID")
|
||||||
|
title: StringProperty(default="", name="Title")
|
||||||
|
type: StringProperty(default="", name="Type")
|
||||||
|
status: StringProperty(default="", name="Status")
|
||||||
|
priority: StringProperty(default="", name="Priority")
|
||||||
|
stage: StringProperty(default="", name="Stage")
|
||||||
|
creation_date: StringProperty(default="", name="Date")
|
||||||
|
creation_author: StringProperty(default="", name="Author")
|
||||||
|
modified_date: StringProperty(default="", name="Modified Date")
|
||||||
|
modified_author: StringProperty(default="", name="Modified By")
|
||||||
|
assigned_to: StringProperty(default="", name="Assigned To")
|
||||||
|
due_date: StringProperty(default="", name="Due Date")
|
||||||
|
description: StringProperty(default="", name="Description")
|
||||||
|
viewpoints: EnumProperty(items=getBcfViewpoints, name="BCF Viewpoints")
|
||||||
|
files: CollectionProperty(name="Files", type=StrProperty)
|
||||||
|
reference_links: CollectionProperty(name="Reference Links", type=StrProperty)
|
||||||
|
labels: CollectionProperty(name="Labels", type=StrProperty)
|
||||||
|
bim_snippet: PointerProperty(type=BcfBimSnippet)
|
||||||
|
document_references: CollectionProperty(name="Document References", type=BcfDocumentReference)
|
||||||
|
related_topics: CollectionProperty(name="Related Topics", type=StrProperty)
|
||||||
|
comments: CollectionProperty(name="Comments", type=BcfComment)
|
||||||
|
|
||||||
|
|
||||||
def refreshBcfTopic(self, context):
|
def refreshBcfTopic(self, context):
|
||||||
RefreshBcfTopic.refresh(context)
|
|
||||||
|
|
||||||
|
|
||||||
class RefreshBcfTopic:
|
|
||||||
props: None
|
|
||||||
topic: None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def refresh(cls, context):
|
|
||||||
|
|
||||||
global bcfviewpoints_enum
|
|
||||||
|
|
||||||
cls.props = bpy.context.scene.BCFProperties
|
|
||||||
cls.topic = bcf.BcfStore.topics[cls.props.active_topic_index][1]
|
|
||||||
|
|
||||||
cls.load_topic_metadata()
|
|
||||||
cls.load_topic_labels()
|
|
||||||
cls.load_topic_files()
|
|
||||||
cls.load_topic_links()
|
|
||||||
cls.load_snippet()
|
|
||||||
cls.load_document_references()
|
|
||||||
cls.load_related_topics()
|
|
||||||
cls.load_viewpoints()
|
|
||||||
cls.load_comments()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_topic_metadata(cls):
|
|
||||||
cls.props.topic_guid = str(cls.topic.xmlId)
|
|
||||||
cls.props.topic_type = cls.topic.type
|
|
||||||
cls.props.topic_status = cls.topic.status
|
|
||||||
cls.props.topic_priority = cls.topic.priority
|
|
||||||
cls.props.topic_stage = cls.topic.stage
|
|
||||||
if cls.topic.date:
|
|
||||||
cls.props.topic_creation_date = cls.topic.date.strftime("%a %Y-%m-%d %H:%S")
|
|
||||||
else:
|
|
||||||
cls.props.topic_creation_date = ""
|
|
||||||
cls.props.topic_creation_author = cls.topic.author
|
|
||||||
if cls.topic.modDate:
|
|
||||||
cls.props.topic_modified_date = cls.topic.modDate.strftime("%a %Y-%m-%d %H:%S")
|
|
||||||
else:
|
|
||||||
cls.props.topic_modified_date = ""
|
|
||||||
cls.props.topic_modified_author = cls.topic.modAuthor
|
|
||||||
cls.props.topic_assigned_to = cls.topic.assignee
|
|
||||||
if cls.topic.dueDate:
|
|
||||||
cls.props.topic_due_date = cls.topic.dueDate.strftime("%a %Y-%m-%d %H:%S")
|
|
||||||
else:
|
|
||||||
cls.props.topic_due_date = ""
|
|
||||||
cls.props.topic_description = cls.topic.description
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_topic_labels(cls):
|
|
||||||
while len(cls.props.topic_labels) > 0:
|
|
||||||
cls.props.topic_labels.remove(0)
|
|
||||||
for label in cls.topic.labels:
|
|
||||||
new = cls.props.topic_labels.add()
|
|
||||||
new.name = label.value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_topic_files(cls):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
while len(cls.props.topic_files) > 0:
|
|
||||||
cls.props.topic_files.remove(0)
|
|
||||||
files = bcfplugin.getRelevantIfcFiles(cls.topic)
|
|
||||||
for f in files:
|
|
||||||
new = cls.props.topic_files.add()
|
|
||||||
new.name = f.filename
|
|
||||||
new.date = f.time.strftime("%a %Y-%m-%d %H:%S")
|
|
||||||
new.reference = f.reference.uri
|
|
||||||
new.ifc_project = f.ifcProjectId
|
|
||||||
new.ifc_spatial = f.ifcSpatialStructureElement
|
|
||||||
new.is_external = f.external
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_topic_links(cls):
|
|
||||||
while len(cls.props.topic_links) > 0:
|
|
||||||
cls.props.topic_links.remove(0)
|
|
||||||
for link in cls.topic.referenceLinks:
|
|
||||||
new = cls.props.topic_links.add()
|
|
||||||
new.name = link.value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_snippet(cls):
|
|
||||||
cls.props.topic_has_snippet = bool(cls.topic.bimSnippet)
|
|
||||||
if cls.topic.bimSnippet:
|
|
||||||
cls.props.topic_snippet_reference = cls.topic.bimSnippet.reference.uri
|
|
||||||
if cls.topic.bimSnippet.schema.uri:
|
|
||||||
cls.props.topic_snippet_schema = cls.topic.bimSnippet.schema.uri
|
|
||||||
cls.props.topic_snippet_type = cls.topic.bimSnippet.type
|
|
||||||
if cls.topic.bimSnippet.external:
|
|
||||||
cls.props.topic_snippet_is_external = cls.topic.bimSnippet.external
|
|
||||||
else:
|
|
||||||
cls.props.topic_snippet_is_external = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_document_references(cls):
|
|
||||||
while len(cls.props.topic_document_references) > 0:
|
|
||||||
cls.props.topic_document_references.remove(0)
|
|
||||||
for doc in cls.topic.docRefs:
|
|
||||||
new = cls.props.topic_document_references.add()
|
|
||||||
new.name = doc.reference.uri
|
|
||||||
new.description = doc.description
|
|
||||||
new.guid = str(doc.guid)
|
|
||||||
new.is_external = doc.external
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_related_topics(cls):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
while len(cls.props.topic_related_topics) > 0:
|
|
||||||
cls.props.topic_related_topics.remove(0)
|
|
||||||
for t in cls.topic.relatedTopics:
|
|
||||||
new = cls.props.topic_related_topics.add()
|
|
||||||
new.name = bcfplugin.getTopicFromUUID(t.value).title
|
|
||||||
new.guid = str(t.value)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_viewpoints(cls):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
bcfviewpoints_enum.clear()
|
|
||||||
bcf.BcfStore.viewpoints = bcfplugin.getViewpoints(cls.topic, realViewpoint=False)
|
|
||||||
for i, viewpoint in enumerate(bcf.BcfStore.viewpoints):
|
|
||||||
bcfviewpoints_enum.append((str(i), "View {}".format(i + 1), ""))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_comments(cls):
|
|
||||||
import bcfplugin
|
|
||||||
|
|
||||||
bcf.BcfStore.comments = bcfplugin.getComments(cls.topic)
|
|
||||||
comments = bpy.data.texts.get("BCF Comments")
|
|
||||||
if comments:
|
|
||||||
comments.clear()
|
|
||||||
else:
|
|
||||||
comments = bpy.data.texts.new("BCF Comments")
|
|
||||||
for i, comment in enumerate(bcf.BcfStore.comments):
|
|
||||||
comments.write("# Comment {} - {}\n".format(i + 1, comment[1].xmlId))
|
|
||||||
comments.write("# From: {} on {}\n".format(comment[1].author, comment[1].date))
|
|
||||||
if comment[1].modDate:
|
|
||||||
comments.write("# Modified by {} on {}\n".format(comment[1].modAuthor, comment[1].modDate))
|
|
||||||
comments.write(comment[1].comment)
|
|
||||||
comments.write("\n\n-----\n\n")
|
|
||||||
|
|
||||||
|
|
||||||
def getBcfViewpoints(self, context):
|
|
||||||
global bcfviewpoints_enum
|
global bcfviewpoints_enum
|
||||||
return bcfviewpoints_enum
|
bcfviewpoints_enum = None
|
||||||
|
|
||||||
|
props = bpy.context.scene.BCFProperties
|
||||||
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
|
topic = props.topics[props.active_topic_index]
|
||||||
|
header = bcfxml.get_header(topic.name)
|
||||||
|
getBcfViewpoints(self, context)
|
||||||
|
|
||||||
|
|
||||||
class PropertySetTemplate(PropertyGroup):
|
class PropertySetTemplate(PropertyGroup):
|
||||||
@@ -1599,32 +1487,13 @@ class BIMProperties(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BCFProperties(PropertyGroup):
|
class BCFProperties(PropertyGroup):
|
||||||
bcf_file: StringProperty(default="", name="BCF File")
|
is_editable: BoolProperty(name="Is Editable", default=False)
|
||||||
|
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||||
|
comment_text_width: IntProperty(name="Comment Text Width", default=40)
|
||||||
|
name: StringProperty(default="", name="Project Name")
|
||||||
|
author: StringProperty(default="john@doe.com", name="Author Email")
|
||||||
topics: CollectionProperty(name="BCF Topics", type=BcfTopic)
|
topics: CollectionProperty(name="BCF Topics", type=BcfTopic)
|
||||||
active_topic_index: IntProperty(name="Active BCF Topic Index", update=refreshBcfTopic)
|
active_topic_index: IntProperty(name="Active BCF Topic Index", update=refreshBcfTopic)
|
||||||
viewpoints: EnumProperty(items=getBcfViewpoints, name="BCF Viewpoints")
|
|
||||||
topic_guid: StringProperty(default="", name="Topic GUID")
|
|
||||||
topic_type: StringProperty(default="", name="Topic Type")
|
|
||||||
topic_status: StringProperty(default="", name="Topic Status")
|
|
||||||
topic_priority: StringProperty(default="", name="Topic Priority")
|
|
||||||
topic_stage: StringProperty(default="", name="Topic Stage")
|
|
||||||
topic_creation_date: StringProperty(default="", name="Topic Date")
|
|
||||||
topic_creation_author: StringProperty(default="", name="Topic Author")
|
|
||||||
topic_modified_date: StringProperty(default="", name="Topic Modified Date")
|
|
||||||
topic_modified_author: StringProperty(default="", name="Topic Modified By")
|
|
||||||
topic_assigned_to: StringProperty(default="", name="Topic Assigned To")
|
|
||||||
topic_due_date: StringProperty(default="", name="Topic Due Date")
|
|
||||||
topic_description: StringProperty(default="", name="Topic Description")
|
|
||||||
topic_labels: CollectionProperty(name="BCF Topic Labels", type=BcfTopicLabel)
|
|
||||||
topic_files: CollectionProperty(name="BCF Topic Files", type=BcfTopicFile)
|
|
||||||
topic_links: CollectionProperty(name="BCF Topic Links", type=BcfTopicLink)
|
|
||||||
topic_has_snippet: BoolProperty(name="BCF Topic Has Snippet", default=False)
|
|
||||||
topic_snippet_reference: StringProperty(name="BIM Snippet Reference")
|
|
||||||
topic_snippet_schema: StringProperty(name="BIM Snippet Schema")
|
|
||||||
topic_snippet_type: StringProperty(name="BIM Snippet Type")
|
|
||||||
topic_snippet_is_external: BoolProperty(name="Is BIM Snippet External")
|
|
||||||
topic_document_references: CollectionProperty(name="BCF Topic Document References", type=BcfTopicDocumentReference)
|
|
||||||
topic_related_topics: CollectionProperty(name="BCF Topic Related Topics", type=BcfTopicRelatedTopic)
|
|
||||||
|
|
||||||
|
|
||||||
class MapConversion(PropertyGroup):
|
class MapConversion(PropertyGroup):
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
|
from . import bcfstore
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from bpy.props import StringProperty
|
from bpy.props import StringProperty
|
||||||
|
|
||||||
@@ -1661,108 +1663,160 @@ class BIM_PT_bcf(Panel):
|
|||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(props, "bcf_file")
|
row.operator("bim.new_bcf_project", text="New Project")
|
||||||
row.operator("bim.select_bcf_file", icon="FILE_FOLDER", text="")
|
row.operator("bim.load_bcf_project", text="Load Project")
|
||||||
|
|
||||||
|
if not props.is_loaded:
|
||||||
|
return
|
||||||
|
|
||||||
|
row.operator("bim.save_bcf_project", text="Save Project")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator("bim.get_bcf_topics")
|
row.prop(props, "name")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "author")
|
||||||
|
|
||||||
props = bpy.context.scene.BCFProperties
|
props = bpy.context.scene.BCFProperties
|
||||||
layout.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
|
row = layout.row()
|
||||||
|
row.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
|
||||||
|
col = row.column(align=True)
|
||||||
|
col.operator("bim.add_bcf_topic", icon="ADD", text="")
|
||||||
|
|
||||||
row = layout.row()
|
if props.active_topic_index < len(props.topics):
|
||||||
row.prop(props, "topic_description", text="")
|
topic = props.topics[props.active_topic_index]
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "viewpoints")
|
|
||||||
row.operator("bim.activate_bcf_viewpoint", icon="SCENE", text="")
|
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_type", text="Type")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_status", text="Status")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_priority", text="Priority")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_stage", text="Stage")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_creation_date", text="Date")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_creation_author", text="Author")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_modified_date", text="Modified On")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_modified_author", text="Modified By")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_assigned_to", text="Assigned To")
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(props, "topic_due_date", text="Due Date")
|
|
||||||
|
|
||||||
layout.label(text="Header Files:")
|
|
||||||
for index, f in enumerate(props.topic_files):
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(f, "name", text="File {} Name".format(index + 1))
|
row.prop(topic, "description", text="")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(f, "reference", text="File {} URI".format(index + 1))
|
row.prop(topic, "viewpoints")
|
||||||
if f.is_external:
|
row.operator("bim.activate_bcf_viewpoint", icon="SCENE", text="")
|
||||||
row.operator("bim.open_bcf_file_reference", icon="URL", text="").data = index
|
|
||||||
else:
|
|
||||||
row.operator("bim.open_bcf_file_reference", icon="FILE_FOLDER", text="").data = "{}/{}".format(
|
|
||||||
props.topic_guid, index
|
|
||||||
)
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(f, "date", text="File {} Date".format(index + 1))
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(f, "ifc_project", text="File {} Project".format(index + 1))
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(f, "ifc_spatial", text="File {} Spatial".format(index + 1))
|
|
||||||
|
|
||||||
layout.label(text="Reference Links:")
|
col = layout.column(align=True)
|
||||||
for index, label in enumerate(props.topic_links):
|
col.prop(topic, "type")
|
||||||
row = layout.row()
|
col.prop(topic, "status")
|
||||||
row.prop(label, "name", text="Link {}".format(index + 1))
|
col.prop(topic, "priority")
|
||||||
row.operator("bim.open_bcf_reference_link", icon="URL", text="").index = index
|
col.prop(topic, "stage")
|
||||||
|
col.prop(topic, "assigned_to")
|
||||||
|
col.prop(topic, "due_date")
|
||||||
|
|
||||||
layout.label(text="Labels:")
|
col = layout.column(align=True)
|
||||||
for index, label in enumerate(props.topic_labels):
|
col.enabled = False
|
||||||
row = layout.row(align=True)
|
col.prop(topic, "creation_date")
|
||||||
row.prop(label, "name", text="")
|
col.prop(topic, "creation_author")
|
||||||
|
col.prop(topic, "modified_date")
|
||||||
|
col.prop(topic, "modified_author")
|
||||||
|
|
||||||
layout.label(text="BIM Snippet:")
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
if props.topic_has_snippet:
|
bcf_topic = bcfxml.topics[topic.name]
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(props, "topic_snippet_type")
|
|
||||||
if props.topic_snippet_schema:
|
|
||||||
row.operator("bim.open_bcf_bim_snippet_schema", icon="URL", text="")
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
if bcf_topic.header:
|
||||||
row.prop(props, "topic_snippet_reference")
|
layout.label(text="Header Files:")
|
||||||
if props.topic_snippet_is_external:
|
for index, f in enumerate(bcf_topic.header.files):
|
||||||
row.operator("bim.open_bcf_bim_snippet_reference", icon="URL", text="")
|
box = self.layout.box()
|
||||||
else:
|
row = box.row(align=True)
|
||||||
row.operator(
|
row.label(text=f.filename, icon="FILE_BLANK")
|
||||||
"bim.open_bcf_bim_snippet_reference", icon="FILE_FOLDER", text=""
|
if f.is_external:
|
||||||
).topic_guid = props.topic_guid
|
row.operator("bim.open_uri", icon="URL", text="").uri = f.reference
|
||||||
|
else:
|
||||||
|
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
|
||||||
|
op.uri = os.path.join(bcfxml.filepath, topic.name, f.reference)
|
||||||
|
box.label(text=f.date)
|
||||||
|
#box.label(text=f.ifc_project)
|
||||||
|
#box.label(text=f.ifc_spatial_structure_element)
|
||||||
|
|
||||||
layout.label(text="Document References:")
|
if topic.reference_links:
|
||||||
for index, doc in enumerate(props.topic_document_references):
|
layout.label(text="Reference Links:")
|
||||||
row = layout.row(align=True)
|
for index, link in enumerate(topic.reference_links):
|
||||||
row.prop(doc, "name", text=f"File {index+1} URI")
|
row = layout.row(align=True)
|
||||||
if doc.is_external:
|
row.prop(link, "name")
|
||||||
row.operator("bim.open_bcf_document_reference", icon="URL", text="").data = "{}/{}".format(
|
row.operator("bim.open_uri", icon="URL", text="").uri = link.name
|
||||||
props.topic_guid, index
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
row.operator("bim.open_bcf_document_reference", icon="FILE_FOLDER", text="").data = "{}/{}".format(
|
|
||||||
props.topic_guid, index
|
|
||||||
)
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(doc, "description", text=f"File {index+1} Description:")
|
|
||||||
|
|
||||||
layout.label(text="Related Topics:")
|
if topic.labels:
|
||||||
for topic in props.topic_related_topics:
|
layout.label(text="Labels:")
|
||||||
row = layout.row(align=True)
|
for index, label in enumerate(topic.labels):
|
||||||
row.operator("bim.view_bcf_topic", text=topic.name).topic_guid = topic.guid
|
row = layout.row(align=True)
|
||||||
|
row.prop(label, "name", text="")
|
||||||
|
|
||||||
|
if topic.bim_snippet.schema:
|
||||||
|
layout.label(text="BIM Snippet:")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(topic.bim_snippet, "type")
|
||||||
|
if topic.bim_snippet.schema:
|
||||||
|
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.schema
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(topic.bim_snippet, "reference")
|
||||||
|
if topic.bim_snippet.is_external:
|
||||||
|
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.reference
|
||||||
|
else:
|
||||||
|
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
|
||||||
|
op.uri = os.path.join(bcfxml.filepath, topic.name, topic.bim_snippet.reference)
|
||||||
|
|
||||||
|
if topic.document_references:
|
||||||
|
layout.label(text="Document References:")
|
||||||
|
for index, doc in enumerate(topic.document_references):
|
||||||
|
box = self.layout.box()
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.prop(doc, "reference")
|
||||||
|
if doc.is_external:
|
||||||
|
row.operator("bim.open_uri", icon="URL", text="").uri = doc.reference
|
||||||
|
else:
|
||||||
|
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
|
||||||
|
op.uri = os.path.join(bcfxml.filepath, topic.name, doc.reference)
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.prop(doc, "description")
|
||||||
|
|
||||||
|
if topic.related_topics:
|
||||||
|
layout.label(text="Related Topics:")
|
||||||
|
for related_topic in topic.related_topics:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.operator("bim.view_bcf_topic", text=related_topic.name).topic_guid = related_topic.name
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_bcf_comments(Panel):
|
||||||
|
bl_label = "BCF Comments"
|
||||||
|
bl_idname = "BIM_PT_bcf_comments"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_parent_id = "BIM_PT_bcf"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
props = bpy.context.scene.BCFProperties
|
||||||
|
|
||||||
|
if props.active_topic_index >= len(props.topics):
|
||||||
|
layout.label(text="No BCF project is loaded")
|
||||||
|
return
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "comment_text_width")
|
||||||
|
|
||||||
|
topic = props.topics[props.active_topic_index]
|
||||||
|
for comment in topic.comments:
|
||||||
|
box = self.layout.box()
|
||||||
|
box.separator()
|
||||||
|
author_text = "{} ({})".format(comment.author, comment.date)
|
||||||
|
if comment.modified_author:
|
||||||
|
author_text = "*{} ({})".format(comment.modified_author, comment.modified_date)
|
||||||
|
box.label(text=author_text, icon="WORDWRAP_ON")
|
||||||
|
box.separator()
|
||||||
|
box.scale_y = 0.5
|
||||||
|
words = comment.comment.split()
|
||||||
|
while words:
|
||||||
|
total_line_chars = 0
|
||||||
|
line_words = []
|
||||||
|
while words and total_line_chars < props.comment_text_width:
|
||||||
|
word = words.pop(0)
|
||||||
|
line_words.append(word)
|
||||||
|
total_line_chars += len(word) + 1 # 1 is for the space
|
||||||
|
box.label(text=" ".join(line_words))
|
||||||
|
box.separator()
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_qa(Panel):
|
class BIM_PT_qa(Panel):
|
||||||
@@ -2096,7 +2150,7 @@ class BIM_UL_topics(bpy.types.UIList):
|
|||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
ob = data
|
ob = data
|
||||||
if item:
|
if item:
|
||||||
layout.prop(item, "name", text="", emboss=False)
|
layout.prop(item, "title", text="", emboss=False)
|
||||||
else:
|
else:
|
||||||
layout.label(text="", translate=False)
|
layout.label(text="", translate=False)
|
||||||
|
|
||||||
|
|||||||
@@ -283,7 +283,9 @@ class IfcClasher:
|
|||||||
continue
|
continue
|
||||||
clashes = clash_set["clashes"]
|
clashes = clash_set["clashes"]
|
||||||
if len(clashes) == 0:
|
if len(clashes) == 0:
|
||||||
|
print(f"Skipping clash set [{clash_set['name']}] since it contains no clash results.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
count_of_input_clashes += len(clashes)
|
count_of_input_clashes += len(clashes)
|
||||||
|
|
||||||
positions = []
|
positions = []
|
||||||
@@ -307,7 +309,13 @@ class IfcClasher:
|
|||||||
if len(pred) == len(clashes.values()):
|
if len(pred) == len(clashes.values()):
|
||||||
i = 0
|
i = 0
|
||||||
for clash in clashes.values():
|
for clash in clashes.values():
|
||||||
clash["smart_group"] = int(pred[i])
|
int_prediction = int(pred[i])
|
||||||
|
if int_prediction == -1:
|
||||||
|
# ungroup this clash since it's a single clash that we were not able to group.
|
||||||
|
new_clash_group_number = np.amax(pred).item() + 1 + i
|
||||||
|
clash["smart_group"] = new_clash_group_number
|
||||||
|
else:
|
||||||
|
clash["smart_group"] = int_prediction
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Create JSON with smart_groups that contain GlobalIDs
|
# Create JSON with smart_groups that contain GlobalIDs
|
||||||
@@ -326,6 +334,17 @@ class IfcClasher:
|
|||||||
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)
|
||||||
|
|
||||||
|
# Rename the clash groups to something more sensible
|
||||||
|
for clash_set, smart_groups in output_clash_sets.items():
|
||||||
|
clash_set_name = clash_set
|
||||||
|
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
||||||
|
i = 1
|
||||||
|
new_smart_group_name = ""
|
||||||
|
for smart_group, global_id_pairs in list(smart_groups[0].items()):
|
||||||
|
new_smart_group_name = f"{clash_set_name} - {i}"
|
||||||
|
smart_groups[0][new_smart_group_name] = smart_groups[0].pop(smart_group)
|
||||||
|
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"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")
|
f"them into {count_of_smart_groups} smart groups in {count_of_final_clash_sets} clash sets")
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ class Selector:
|
|||||||
value = filter_rule.children[2].children[0][1:-1]
|
value = filter_rule.children[2].children[0][1:-1]
|
||||||
for element in elements:
|
for element in elements:
|
||||||
element_value = self.get_element_value(element, key)
|
element_value = self.get_element_value(element, key)
|
||||||
if not element_value:
|
if element_value is None:
|
||||||
continue
|
continue
|
||||||
if not comparison or self.filter_element(element, element_value, comparison, value):
|
if not comparison or self.filter_element(element, element_value, comparison, value):
|
||||||
results.append(element)
|
results.append(element)
|
||||||
|
|||||||
@@ -1304,7 +1304,7 @@ void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::Ar
|
|||||||
//
|
//
|
||||||
#ifdef USE_MMAP
|
#ifdef USE_MMAP
|
||||||
IfcFile::IfcFile(const std::string& fn, bool mmap) {
|
IfcFile::IfcFile(const std::string& fn, bool mmap) {
|
||||||
return IfcFile::Init(new IfcSpfStream(fn, mmap));
|
initialize_(new IfcSpfStream(fn, mmap));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
IfcFile::IfcFile(const std::string& fn) {
|
IfcFile::IfcFile(const std::string& fn) {
|
||||||
|
|||||||
Reference in New Issue
Block a user