diff --git a/src/bcf/bcf/v2/visinfo.py b/src/bcf/bcf/v2/visinfo.py index 976cb02fd2..359fbc0ab9 100644 --- a/src/bcf/bcf/v2/visinfo.py +++ b/src/bcf/bcf/v2/visinfo.py @@ -1,6 +1,6 @@ import uuid import zipfile -from typing import Any, Iterable, Optional +from typing import Any, Iterable, Optional, Literal, Union import numpy as np from ifcopenshell import entity_instance @@ -204,6 +204,104 @@ class VisualizationInfoHandler: visualization_info=build_viewpoint_from_position_and_guids(position, *guids), xml_handler=xml_handler ) + def get_selected_guids(self) -> Union[list[str], None]: + """ + Return viewpoint selected elements IFC guids. + + Returns: + If viewpoint has no selection settings, return `None`. + Otherwise return a list of selected elements IFC guids. + """ + visualization_info = self.visualization_info + components = visualization_info.components + if not components: + return None + + selection = components.selection + if not selection: + return None + return [guid for c in selection.component if (guid := c.ifc_guid)] + + def set_selected_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + visualization_info = self.visualization_info + + guids = [e.GlobalId for e in elements] + components = visualization_info.components + if not components: + visibility = mdl.ComponentVisibility(default_visibility=True) + components = mdl.Components(visibility=visibility) + visualization_info.components = components + + selection = components.selection + components_list = [mdl.Component(ifc_guid=guid) for guid in guids] + if not selection: + selection = mdl.ComponentSelection() + components.selection = selection + selection.component = components_list + + def set_visible_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + self.set_visibility(elements, elements_visibility="VISIBLE") + + def set_hidden_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + self.set_visibility(elements, elements_visibility="HIDDEN") + + def set_visibility( + self, elements: list[ifcopenshell.entity_instance], elements_visibility: Literal["VISIBLE", "HIDDEN"] + ) -> None: + visualization_info = self.visualization_info + default_visibility = elements_visibility == "HIDDEN" + + guids = [e.GlobalId for e in elements] + components_list = [mdl.Component(ifc_guid=guid) for guid in guids] + components = visualization_info.components + if not components: + visibility = mdl.ComponentVisibility(default_visibility=default_visibility) + components = mdl.Components(visibility=visibility) + visualization_info.components = components + + visibility = components.visibility + if not visibility: + visibility = mdl.ComponentVisibility(default_visibility=default_visibility) + components.visibility = visibility + elif visibility.default_visibility != default_visibility: + visibility.default_visibility = default_visibility + + exceptions = visibility.exceptions + if not exceptions: + exceptions = mdl.ComponentVisibilityExceptions() + visibility.exceptions = exceptions + exceptions.component = components_list + + def get_elements_visibility(self) -> Union[tuple[bool, list[str]], None]: + """ + Return viewpoint elements visibility settings. + + Returns: + If viewpoint has no visibility settings, return `None`. + Otherwise return a tuple containing the default visibility + and a list of IFC element GUIDs listed as exceptions. + + If default visibility is `True`, all elements are visible except the exceptions. + + If default visibility is `False`, all elements are hidden except the exceptions. + """ + + visualization_info = self.visualization_info + components = visualization_info.components + if not components: + return None + + visibility = components.visibility + if not visibility: + return None + default_visibility = visibility.default_visibility or False + + exceptions = visibility.exceptions + if not exceptions: + return default_visibility, [] + guids = [guid for c in exceptions.component if (guid := c.ifc_guid)] + return default_visibility, guids + def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo: """ @@ -256,7 +354,7 @@ def build_components(*guids: str) -> mdl.Components: Return the BCF components from an IFC element GUID. Args: - *guids: One or more IFC element GUID. + *guids: One or more selected IFC element GUID. Returns: The BCF components definition. diff --git a/src/bcf/bcf/v3/visinfo.py b/src/bcf/bcf/v3/visinfo.py index 6eb2b933bd..6fb2be6a39 100644 --- a/src/bcf/bcf/v3/visinfo.py +++ b/src/bcf/bcf/v3/visinfo.py @@ -1,6 +1,6 @@ import uuid import zipfile -from typing import Any, Iterable, Optional +from typing import Any, Iterable, Optional, Literal, Union import numpy as np from ifcopenshell import entity_instance @@ -204,6 +204,104 @@ class VisualizationInfoHandler: visualization_info=build_viewpoint_from_position_and_guids(position, *guids), xml_handler=xml_handler ) + def get_selected_guids(self) -> Union[list[str], None]: + """ + Return viewpoint selected elements IFC guids. + + Returns: + If viewpoint has no selection settings, return `None`. + Otherwise return a list of selected elements IFC guids. + """ + visualization_info = self.visualization_info + components = visualization_info.components + if not components: + return None + + selection = components.selection + if not selection: + return None + return [guid for c in selection.component if (guid := c.ifc_guid)] + + def set_selected_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + visualization_info = self.visualization_info + + guids = [e.GlobalId for e in elements] + components = visualization_info.components + if not components: + visibility = mdl.ComponentVisibility(default_visibility=True) + components = mdl.Components(visibility=visibility) + visualization_info.components = components + + selection = components.selection + components_list = [mdl.Component(ifc_guid=guid) for guid in guids] + if not selection: + selection = mdl.ComponentSelection() + components.selection = selection + selection.component = components_list + + def set_visible_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + self.set_visibility(elements, elements_visibility="VISIBLE") + + def set_hidden_elements(self, elements: list[ifcopenshell.entity_instance]) -> None: + self.set_visibility(elements, elements_visibility="HIDDEN") + + def set_visibility( + self, elements: list[ifcopenshell.entity_instance], elements_visibility: Literal["VISIBLE", "HIDDEN"] + ) -> None: + visualization_info = self.visualization_info + default_visibility = elements_visibility == "HIDDEN" + + guids = [e.GlobalId for e in elements] + components_list = [mdl.Component(ifc_guid=guid) for guid in guids] + components = visualization_info.components + if not components: + visibility = mdl.ComponentVisibility(default_visibility=default_visibility) + components = mdl.Components(visibility=visibility) + visualization_info.components = components + + visibility = components.visibility + if not visibility: + visibility = mdl.ComponentVisibility(default_visibility=default_visibility) + components.visibility = visibility + elif visibility.default_visibility != default_visibility: + visibility.default_visibility = default_visibility + + exceptions = visibility.exceptions + if not exceptions: + exceptions = mdl.ComponentVisibilityExceptions() + visibility.exceptions = exceptions + exceptions.component = components_list + + def get_elements_visibility(self) -> Union[tuple[bool, list[str]], None]: + """ + Return viewpoint elements visibility settings. + + Returns: + If viewpoint has no visibility settings, return `None`. + Otherwise return a tuple containing the default visibility + and a list of IFC element GUIDs listed as exceptions. + + If default visibility is `True`, all elements are visible except the exceptions. + + If default visibility is `False`, all elements are hidden except the exceptions. + """ + + visualization_info = self.visualization_info + components = visualization_info.components + if not components: + return None + + visibility = components.visibility + if not visibility: + return None + default_visibility = visibility.default_visibility or False + + exceptions = visibility.exceptions + if not exceptions: + return default_visibility, [] + guids = [guid for c in exceptions.component if (guid := c.ifc_guid)] + return default_visibility, guids + def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo: """ @@ -256,7 +354,7 @@ def build_components(*guids: str) -> mdl.Components: Return the BCF components from an IFC element GUID. Args: - *guids: One or more IFC element GUID. + *guids: One or more selected IFC element GUID. Returns: The BCF components definition. diff --git a/src/blenderbim/blenderbim/bim/module/bcf/operator.py b/src/blenderbim/blenderbim/bim/module/bcf/operator.py index b30864f36c..95d9a9da4d 100644 --- a/src/blenderbim/blenderbim/bim/module/bcf/operator.py +++ b/src/blenderbim/blenderbim/bim/module/bcf/operator.py @@ -31,6 +31,7 @@ import webbrowser import ifcopenshell import ifcopenshell.util.geolocation import ifcopenshell.util.unit +import blenderbim.tool as tool import blenderbim.bim.module.bcf.prop as bcf_prop import blenderbim.bim.module.bcf.bcfstore as bcfstore from pathlib import Path @@ -453,6 +454,7 @@ class AddBcfViewpoint(bpy.types.Operator): def execute(self, context): bcfxml = bcfstore.BcfStore.get_bcfxml() blender_camera = context.scene.camera + assert blender_camera props = context.scene.BCFProperties blender_topic = props.active_topic topic = bcfxml.topics[blender_topic.name] @@ -495,13 +497,27 @@ class AddBcfViewpoint(bpy.types.Operator): snapshot = f.read() # viewpoint.snapshot = blender_render.filepath - # TODO we should handle selection and visibility state vizinfo = bcf.v2.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot) topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo topic.markup.viewpoints.append( bcf.v2.model.ViewPoint(viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png") ) + def get_ifc_elements(objs: list[bpy.types.Object]) -> list[ifcopenshell.entity_instance]: + elements = [] + for obj in objs: + if e := tool.Ifc.get_entity(obj): + elements.append(e) + return elements + + selected_elements = get_ifc_elements(context.selected_objects) + if selected_elements: + vizinfo.set_selected_elements(selected_elements) + + visible_elements = get_ifc_elements(context.visible_objects) + if visible_elements: + vizinfo.set_visible_elements(visible_elements) + blender_render.filepath = old_filepath blender_render.image_settings.file_format = old_file_format props.refresh_topic(context)