diff --git a/src/blenderbim/blenderbim/bim/module/project/__init__.py b/src/blenderbim/blenderbim/bim/module/project/__init__.py index 8198e60695..b1d85e3194 100644 --- a/src/blenderbim/blenderbim/bim/module/project/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/project/__init__.py @@ -39,6 +39,7 @@ classes = ( operator.LoadProject, operator.LoadProjectElements, operator.NewProject, + operator.QueryLinkedElement, operator.RefreshLibrary, operator.ReloadLink, operator.RevertProject, diff --git a/src/blenderbim/blenderbim/bim/module/project/data.py b/src/blenderbim/blenderbim/bim/module/project/data.py index e4b6acaa92..5524212bab 100644 --- a/src/blenderbim/blenderbim/bim/module/project/data.py +++ b/src/blenderbim/blenderbim/bim/module/project/data.py @@ -65,5 +65,6 @@ class ProjectData: class LinksData: + linked_data = {} enable_culling = False is_loaded = False diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 5b4e93b349..bfaf43f0c9 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -1469,6 +1469,133 @@ class LoadLinkedProject(bpy.types.Operator): self.collection.objects.link(obj) +class QueryLinkedElement(bpy.types.Operator): + bl_idname = "bim.query_linked_element" + bl_label = "Query Linked Element" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return context.area.type == "VIEW_3D" + + def execute(self, context): + import sqlite3 + from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d + + LinksData.linked_data = {} + bpy.context.scene.BIMProjectProperties.queried_obj = None + + for area in bpy.context.screen.areas: + if area.type == "PROPERTIES": + for region in area.regions: + if region.type == "WINDOW": + region.tag_redraw() + elif area.type == "VIEW_3D": + area.tag_redraw() + + region = context.region + rv3d = context.region_data + coord = (self.mouse_x, self.mouse_y) + origin = region_2d_to_origin_3d(region, rv3d, coord) + direction = region_2d_to_vector_3d(region, rv3d, coord) + hit, location, normal, face_index, obj, matrix = self.ray_cast(context, origin, direction) + if not hit: + self.report({"INFO"}, "No object found.") + return {"FINISHED"} + + if "guids" not in obj: + self.report({"INFO"}, "Object is not a linked IFC element.") + return {"FINISHED"} + + guid = None + guid_start_index = 0 + for i, guid_end_index in enumerate(obj["guid_ids"]): + if face_index < guid_end_index: + guid = obj["guids"][i] + bpy.context.scene.BIMProjectProperties.queried_obj = obj + + selected_tris = [] + selected_edges = [] + vert_indices = set() + for polygon in obj.data.polygons[guid_start_index:guid_end_index]: + vert_indices.update(polygon.vertices) + vert_indices = list(vert_indices) + vert_map = {k: v for v, k in enumerate(vert_indices)} + selected_vertices = [tuple(obj.matrix_world @ obj.data.vertices[vi].co) for vi in vert_indices] + for polygon in obj.data.polygons[guid_start_index:guid_end_index]: + selected_tris.append(tuple(vert_map[v] for v in polygon.vertices)) + selected_edges.extend(tuple([vert_map[vi] for vi in e] for e in polygon.edge_keys)) + + obj["selected_vertices"] = selected_vertices + obj["selected_edges"] = selected_edges + obj["selected_tris"] = selected_tris + + break + guid_start_index = guid_end_index + + self.db = sqlite3.connect(obj["db"]) + self.c = self.db.cursor() + + self.c.execute(f"SELECT * FROM elements WHERE global_id = '{guid}' LIMIT 1") + element = self.c.fetchone() + + attributes = {} + for i, attr in enumerate(["GlobalId", "IFC Class", "Predefined Type", "Name", "Description"]): + if element[i + 1] is not None: + attributes[attr] = element[i + 1] + + self.c.execute("SELECT * FROM properties WHERE element_id = ?", (element[0],)) + rows = self.c.fetchall() + + properties = {} + for row in rows: + properties.setdefault(row[1], {})[row[2]] = row[3] + + self.c.execute("SELECT * FROM relationships WHERE from_id = ?", (element[0],)) + relationships = self.c.fetchall() + + relating_type_id = None + + for relationship in relationships: + if relationship[1] == "IfcRelDefinesByType": + relating_type_id = relationship[2] + + type_properties = {} + if relating_type_id is not None: + self.c.execute("SELECT * FROM properties WHERE element_id = ?", (relating_type_id,)) + rows = self.c.fetchall() + for row in rows: + type_properties.setdefault(row[1], {})[row[2]] = row[3] + + LinksData.linked_data = { + "attributes": attributes, + "properties": [(k, properties[k]) for k in sorted(properties.keys())], + "type_properties": [(k, type_properties[k]) for k in sorted(type_properties.keys())], + } + + for area in bpy.context.screen.areas: + if area.type == "PROPERTIES": + for region in area.regions: + if region.type == "WINDOW": + region.tag_redraw() + elif area.type == "VIEW_3D": + area.tag_redraw() + + self.report({"INFO"}, f"Loaded data for {guid}") + ProjectDecorator.install(bpy.context) + return {"FINISHED"} + + def ray_cast(self, context, origin, direction): + depsgraph = context.evaluated_depsgraph_get() + result = context.scene.ray_cast(depsgraph, origin, direction) + return result + + def invoke(self, context, event): + self.mouse_x = event.mouse_region_x + self.mouse_y = event.mouse_region_y + return self.execute(context) + + class EnableCulling(bpy.types.Operator): bl_idname = "bim.enable_culling" bl_label = "Enable Culling" diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py index 2a7ccb7360..cd968e8173 100644 --- a/src/blenderbim/blenderbim/bim/module/project/prop.py +++ b/src/blenderbim/blenderbim/bim/module/project/prop.py @@ -170,6 +170,7 @@ class BIMProjectProperties(PropertyGroup): export_schema: EnumProperty(items=get_export_schema, name="IFC Schema") template_file: EnumProperty(items=get_template_file, name="Template File") use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False) + queried_obj: bpy.props.PointerProperty(type=bpy.types.Object) def get_library_element_index(self, lib_element): return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element)) diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py index f71755deae..361b447ba3 100644 --- a/src/blenderbim/blenderbim/bim/module/project/ui.py +++ b/src/blenderbim/blenderbim/bim/module/project/ui.py @@ -21,7 +21,7 @@ import blenderbim.bim from blenderbim.bim.helper import prop_with_search from bpy.types import Panel, Menu, UIList from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.project.data import ProjectData +from blenderbim.bim.module.project.data import ProjectData, LinksData class BIM_MT_project(Menu): @@ -307,6 +307,41 @@ class BIM_PT_links(Panel): "active_link_index", ) + if LinksData.enable_culling: + row = self.layout.row(align=True) + row.label(text="Object Culling Enabled", icon="MOD_TRIANGULATE") + + if not LinksData.linked_data: + row = self.layout.row(align=True) + row.label(text="No Object Selected", icon="QUESTION") + return + + for name, value in LinksData.linked_data["attributes"].items(): + row = self.layout.row(align=True) + row.label(text=name) + row.label(text=value) + + for pset in LinksData.linked_data["properties"]: + box = self.layout.box() + row = box.row(align=True) + row.label(text=pset[0], icon="COPY_ID") + for name, value in pset[1].items(): + row = box.row(align=True) + row.label(text=name) + row.label(text=value) + + if LinksData.linked_data["type_properties"]: + row = self.layout.row() + row.label(icon="LINKED", text="Type Properties") + for pset in LinksData.linked_data["type_properties"]: + box = self.layout.box() + row = box.row(align=True) + row.label(text=pset[0], icon="COPY_ID") + for name, value in pset[1].items(): + row = box.row(align=True) + row.label(text=name) + row.label(text=value) + class BIM_UL_library(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):