bonsai bcf v3 - support working with viewpoints #2790

Also fixed a bug when removing viewpoint always removed the last viewpoint
This commit is contained in:
Andrej730
2024-08-20 18:18:37 +05:00
parent 8eae997053
commit 64b96633c2
4 changed files with 148 additions and 48 deletions
+3
View File
@@ -3,5 +3,8 @@ import bcf.v3.model
from typing import Union from typing import Union
BimSnippet = Union[bcf.v2.model.BimSnippet, bcf.v3.model.BimSnippet] BimSnippet = Union[bcf.v2.model.BimSnippet, bcf.v3.model.BimSnippet]
BitMap = Union[bcf.v2.model.VisualizationInfoBitmap, bcf.v3.model.Bitmap]
DocumentReference = Union[bcf.v2.model.TopicDocumentReference, bcf.v3.model.DocumentReference] DocumentReference = Union[bcf.v2.model.TopicDocumentReference, bcf.v3.model.DocumentReference]
HeaderFile = Union[bcf.v2.model.HeaderFile, bcf.v3.model.File] HeaderFile = Union[bcf.v2.model.HeaderFile, bcf.v3.model.File]
Topic = Union[bcf.v2.model.Topic, bcf.v3.model.Topic]
ViewPoint = Union[bcf.v2.model.ViewPoint, bcf.v3.model.ViewPoint]
+10 -2
View File
@@ -15,7 +15,7 @@ TopicHandler = Union[bcf.v2.topic.TopicHandler, bcf.v3.topic.TopicHandler]
def extract_file( def extract_file(
topic: TopicHandler, topic: TopicHandler,
entity: Union[mdl.HeaderFile, mdl.BimSnippet, mdl.DocumentReference], entity: Union[mdl.HeaderFile, mdl.BimSnippet, mdl.DocumentReference, mdl.BitMap],
bcfxml: Optional[Union[bcf.v2.bcfxml.BcfXml, bcf.v3.bcfxml.BcfXml]] = None, bcfxml: Optional[Union[bcf.v2.bcfxml.BcfXml, bcf.v3.bcfxml.BcfXml]] = None,
outfile: Optional[Path] = None, outfile: Optional[Path] = None,
) -> Union[Path, str, None]: ) -> Union[Path, str, None]:
@@ -47,7 +47,8 @@ def extract_file(
return None return None
# For v3 document references external documents are detected by empty document_guid. # For v3 document references external documents are detected by empty document_guid.
if not isinstance(entity, bcf.v3.model.DocumentReference) and entity.is_external: # External bitmaps are not supported by bcf.
if not isinstance(entity, (bcf.v3.model.DocumentReference, mdl.BitMap)) and entity.is_external:
return reference return reference
if isinstance(entity, bcf.v3.model.DocumentReference): if isinstance(entity, bcf.v3.model.DocumentReference):
@@ -72,6 +73,13 @@ def extract_file(
assert isinstance(bytes_data, bytes) assert isinstance(bytes_data, bytes)
elif isinstance(entity, mdl.HeaderFile): elif isinstance(entity, mdl.HeaderFile):
bytes_data = topic.reference_files[reference] bytes_data = topic.reference_files[reference]
elif isinstance(entity, mdl.BitMap):
bytes_data = next(
byte_data
for vp in topic.viewpoints.values()
for data_reference, byte_data in vp.bitmaps.items()
if data_reference == reference
)
elif isinstance(entity, bcf.v2.model.TopicDocumentReference): elif isinstance(entity, bcf.v2.model.TopicDocumentReference):
assert isinstance(topic, bcf.v2.topic.TopicHandler) assert isinstance(topic, bcf.v2.topic.TopicHandler)
bytes_data = topic.document_references[reference] bytes_data = topic.document_references[reference]
+102 -46
View File
@@ -22,6 +22,7 @@ import bcf.v3.bcfxml
import bcf.v3.document import bcf.v3.document
import bcf.v3.model import bcf.v3.model
import bcf.v3.topic import bcf.v3.topic
import bcf.v3.visinfo
import bpy import bpy
import bcf import bcf
import bcf.bcfxml import bcf.bcfxml
@@ -29,8 +30,8 @@ import bcf.v2.bcfxml
import bcf.v2.model import bcf.v2.model
import bcf.v2.topic import bcf.v2.topic
import bcf.v2.visinfo import bcf.v2.visinfo
import bcf.agnostic.visinfo
import bcf.agnostic.topic import bcf.agnostic.topic
import bcf.agnostic.visinfo
import uuid import uuid
import numpy as np import numpy as np
import tempfile import tempfile
@@ -532,13 +533,11 @@ class AddBcfViewpoint(bpy.types.Operator):
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
return {"FINISHED"}
blender_camera = context.scene.camera blender_camera = context.scene.camera
assert blender_camera assert blender_camera
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.active_topic blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
@@ -546,32 +545,66 @@ class AddBcfViewpoint(bpy.types.Operator):
direction = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0)) direction = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
up = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0)) up = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
camera_view_point = bcf.v2.model.Point( blender_render = context.scene.render
x=blender_camera.location.x, y=blender_camera.location.y, z=blender_camera.location.z assert isinstance(blender_camera.data, bpy.types.Camera)
) visinfo_guid = str(uuid.uuid4())
camera_direction = bcf.v2.model.Direction(x=direction.x, y=direction.y, z=direction.z) if bcf_v2:
camera_up_vector = bcf.v2.model.Direction(x=up.x, y=up.y, z=up.z) camera_view_point = bcf.v2.model.Point(
if blender_camera.data.type == "ORTHO": x=blender_camera.location.x, y=blender_camera.location.y, z=blender_camera.location.z
camera = bcf.v2.model.OrthogonalCamera(
view_to_world_scale=blender_camera.data.ortho_scale,
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
) )
visualization_info = bcf.v2.model.VisualizationInfo(orthogonal_camera=camera) camera_direction = bcf.v2.model.Direction(x=direction.x, y=direction.y, z=direction.z)
elif blender_camera.data.type == "PERSP": camera_up_vector = bcf.v2.model.Direction(x=up.x, y=up.y, z=up.z)
camera = bcf.v2.model.PerspectiveCamera( if blender_camera.data.type == "ORTHO":
field_of_view=degrees(blender_camera.data.angle), camera = bcf.v2.model.OrthogonalCamera(
camera_view_point=camera_view_point, view_to_world_scale=blender_camera.data.ortho_scale,
camera_direction=camera_direction, camera_view_point=camera_view_point,
camera_up_vector=camera_up_vector, camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(guid=visinfo_guid, orthogonal_camera=camera)
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.model.PerspectiveCamera(
field_of_view=degrees(blender_camera.data.angle),
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(guid=visinfo_guid, perspective_camera=camera)
else:
self.report({"INFO"}, f"Unsupported camera type: '{blender_camera.data.type}'.")
return {"FINISHED"}
else:
camera_view_point = bcf.v3.model.Point(
x=blender_camera.location.x, y=blender_camera.location.y, z=blender_camera.location.z
) )
visualization_info = bcf.v2.model.VisualizationInfo(guid=str(uuid.uuid4()), perspective_camera=camera) camera_direction = bcf.v3.model.Direction(x=direction.x, y=direction.y, z=direction.z)
camera_up_vector = bcf.v3.model.Direction(x=up.x, y=up.y, z=up.z)
cam_aspect = blender_render.resolution_x / blender_render.resolution_y
if blender_camera.data.type == "ORTHO":
camera = bcf.v3.model.OrthogonalCamera(
view_to_world_scale=blender_camera.data.ortho_scale,
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
aspect_ratio=cam_aspect,
)
visualization_info = bcf.v3.model.VisualizationInfo(guid=visinfo_guid, orthogonal_camera=camera)
elif blender_camera.data.type == "PERSP":
camera = bcf.v3.model.PerspectiveCamera(
field_of_view=degrees(blender_camera.data.angle),
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
aspect_ratio=cam_aspect,
)
visualization_info = bcf.v3.model.VisualizationInfo(guid=visinfo_guid, perspective_camera=camera)
else:
self.report({"INFO"}, f"Unsupported camera type: '{blender_camera.data.type}'.")
return {"FINISHED"}
# TODO allow the user to enable or disable snapshotting # TODO allow the user to enable or disable snapshotting
snapshot = None snapshot = None
blender_render = context.scene.render
old_file_format = blender_render.image_settings.file_format old_file_format = blender_render.image_settings.file_format
blender_render.image_settings.file_format = "PNG" blender_render.image_settings.file_format = "PNG"
old_filepath = blender_render.filepath old_filepath = blender_render.filepath
@@ -581,11 +614,26 @@ class AddBcfViewpoint(bpy.types.Operator):
snapshot = f.read() snapshot = f.read()
# viewpoint.snapshot = blender_render.filepath # viewpoint.snapshot = blender_render.filepath
vizinfo = bcf.v2.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot) if isinstance(visualization_info, bcf.v2.model.VisualizationInfo):
topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo vizinfo = bcf.v2.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot)
topic.markup.viewpoints.append( assert isinstance(topic, bcf.v2.topic.TopicHandler)
bcf.v2.model.ViewPoint(viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png") topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo
) viewpoints = tool.Bcf.get_topic_viewpoints(topic)
viewpoint = bcf.v2.model.ViewPoint(
viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png"
)
assert tool.Bcf.is_list_of(viewpoints, bcf.v2.model.ViewPoint)
viewpoints.append(viewpoint)
else:
vizinfo = bcf.v3.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot)
assert isinstance(topic, bcf.v3.topic.TopicHandler)
topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo
viewpoints = tool.Bcf.get_topic_viewpoints(topic)
viewpoint = bcf.v3.model.ViewPoint(
viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png"
)
assert tool.Bcf.is_list_of(viewpoints, bcf.v3.model.ViewPoint)
viewpoints.append(viewpoint)
def get_ifc_elements(objs: list[bpy.types.Object]) -> list[ifcopenshell.entity_instance]: def get_ifc_elements(objs: list[bpy.types.Object]) -> list[ifcopenshell.entity_instance]:
elements = [] elements = []
@@ -615,27 +663,35 @@ class RemoveBcfViewpoint(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return bcf_prop.getBcfViewpoints(None, context) bcfxml = bcfstore.BcfStore.get_bcfxml()
if not bcfxml:
return False
props = context.scene.BCFProperties
topic = props.active_topic
if not topic:
return False
topic = props.topics[topic.name]
if not tool.Blender.get_enum_safe(topic, "viewpoints"):
cls.poll_message_set("No viewpoint selected.")
return False
return True
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml assert bcfxml
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
return {"FINISHED"}
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.active_topic blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
for key, viewpoint in topic.viewpoints.items(): del topic.viewpoints[blender_topic.viewpoints]
if viewpoint.guid == blender_topic.viewpoints:
break viewpoints = tool.Bcf.get_topic_viewpoints(topic)
del topic.viewpoints[key] # Only guid is required attribute for a viewpoint.
for i, viewpoint in enumerate(topic.markup.viewpoints): vp_index = next(i for i, vp in enumerate(viewpoints) if vp.guid in blender_topic.viewpoints)
if viewpoint.guid == blender_topic.viewpoints: del viewpoints[vp_index]
break tool.Bcf.set_topic_viewpoints(topic, viewpoints)
del topic.markup.viewpoints[i]
props.refresh_topic(context) props.refresh_topic(context)
return {"FINISHED"} return {"FINISHED"}
@@ -716,8 +772,8 @@ class AddBcfDocumentReference(bpy.types.Operator):
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.active_topic blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
@@ -1102,6 +1158,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
if blender_topic is None: if blender_topic is None:
return False return False
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
return topic.viewpoints return topic.viewpoints
@@ -1386,9 +1443,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
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(bcfxml.filepath, topic.guid, bitmap.reference)) # image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
# TODO: suuport bcf v3.
with tempfile.NamedTemporaryFile(delete=False) as f: with tempfile.NamedTemporaryFile(delete=False) as f:
topic.extract_file(bitmap, f.name) bcf.agnostic.topic.extract_file(topic, bitmap, outfile=Path(f.name))
# f.write(bitmap.what) # f.write(bitmap.what)
image = bpy.data.images.load(f.name) image = bpy.data.images.load(f.name)
src_width = image.size[0] src_width = image.size[0]
+33
View File
@@ -219,6 +219,39 @@ class Bcf(bonsai.core.tool.Bcf):
topic_.related_topics = (topic_related_topics := bcf.v3.model.TopicRelatedTopics()) topic_.related_topics = (topic_related_topics := bcf.v3.model.TopicRelatedTopics())
topic_related_topics.related_topic = related_topics topic_related_topics.related_topic = related_topics
@classmethod
def get_topic_viewpoints(
cls, topic: bcf.agnostic.topic.TopicHandler
) -> Union[list[bcf.v2.model.ViewPoint], list[bcf.v3.model.ViewPoint]]:
if isinstance(topic, bcf.v2.topic.TopicHandler):
assert topic.markup
viewpoints = topic.markup.viewpoints
else:
topic_viewpoints = topic.topic.viewpoints
if not topic_viewpoints:
return []
viewpoints = topic_viewpoints.view_point
return viewpoints
@classmethod
def set_topic_viewpoints(
cls,
topic: bcf.agnostic.topic.TopicHandler,
viewpoints: Union[list[bcf.v2.model.ViewPoint], list[bcf.v3.model.ViewPoint]],
) -> None:
if isinstance(topic, bcf.v2.topic.TopicHandler):
assert topic.markup
assert cls.is_list_of(viewpoints, bcf.v2.model.ViewPoint)
topic.markup.viewpoints = viewpoints
else:
topic_viewpoints = topic.topic.viewpoints
if not topic_viewpoints:
if not viewpoints:
return
topic.topic.viewpoints = (topic_viewpoints := bcf.v3.model.TopicViewpoints())
assert cls.is_list_of(viewpoints, bcf.v3.model.ViewPoint)
topic_viewpoints.view_point = viewpoints
## visinfo ## visinfo
@classmethod @classmethod
def get_viewpoint_bitmaps( def get_viewpoint_bitmaps(