New workspace tool to query chunked models

This commit is contained in:
Dion Moult
2024-02-23 23:05:48 +11:00
parent d7a7701005
commit d02239902f
5 changed files with 184 additions and 24 deletions
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import ui, prop, operator
from . import ui, prop, operator, workspace
classes = (
operator.AppendEntireLibrary,
@@ -54,6 +54,7 @@ classes = (
operator.zxc,
operator.aaa,
operator.asdfasdf,
operator.QueryLinkedElement,
prop.LibraryElement,
prop.FilterCategory,
prop.Link,
@@ -74,6 +75,8 @@ addon_keymaps = []
def register():
if not bpy.app.background:
bpy.utils.register_tool(workspace.QueryTool, after={"builtin.select"}, separator=True, group=False)
bpy.types.Scene.BIMProjectProperties = bpy.props.PointerProperty(type=prop.BIMProjectProperties)
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
bpy.types.TOPBAR_MT_file_context_menu.prepend(ui.file_menu)
@@ -93,6 +96,8 @@ def register():
def unregister():
if not bpy.app.background:
bpy.utils.unregister_tool(workspace.QueryTool)
bpy.types.TOPBAR_MT_file.remove(ui.file_menu)
bpy.types.TOPBAR_MT_file_context_menu.remove(ui.file_menu)
del bpy.types.Scene.BIMProjectProperties
@@ -24,6 +24,7 @@ from blenderbim.bim.ifc import IfcStore
def refresh():
ProjectData.is_loaded = False
LinksData.is_loaded = False
class ProjectData:
@@ -61,3 +62,8 @@ class ProjectData:
return f"{save_date} {':'.join(save_time.split(':')[0:2])}"
except:
return ""
class LinksData:
linked_data = {}
is_loaded = False
@@ -21,6 +21,7 @@ import bpy
import time
import logging
import tempfile
import numpy as np
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.selector
@@ -37,7 +38,8 @@ from blenderbim.bim import import_ifc
from blenderbim.bim import export_ifc
from pathlib import Path
from bpy.app.handlers import persistent
import numpy as np
from blenderbim.bim.module.project.data import LinksData
from blenderbim.bim.module.project.decorator import ProjectDecorator
class NewProject(bpy.types.Operator):
@@ -1146,13 +1148,14 @@ class xxx(bpy.types.Operator):
import numpy as np
from mathutils import Matrix
import resource
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
start = time.time()
collection = bpy.data.collections.new("Project")
# ifc_file = ifcopenshell.open('/home/dion/test.ifc')
# ifc_file = ifcopenshell.open('/home/dion/drive/ifcs/racbasicsampleproject.ifc')
ifc_file = ifcopenshell.open('/home/dion/drive/ifcs/TXG_sample_project-fixed-IFC4.ifc')
ifc_file = ifcopenshell.open("/home/dion/drive/ifcs/TXG_sample_project-fixed-IFC4.ifc")
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
@@ -1226,9 +1229,9 @@ class xxx(bpy.types.Operator):
break
bpy.context.scene.collection.children.link(collection)
print('Finished', time.time() - start)
print("Finished", time.time() - start)
newmem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Mem", newmem-mem)
print("Mem", newmem - mem)
return {"FINISHED"}
@@ -1247,11 +1250,12 @@ class zzz(bpy.types.Operator):
import time
from mathutils import Matrix
import resource
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
start = time.time()
collection = bpy.data.collections.new("Project")
model = h5py.File('/home/dion/test3.h5', 'r')
model = h5py.File("/home/dion/test3.h5", "r")
# model = h5py.File('/home/dion/cpp.h5', 'r')
# model = h5py.File('/home/dion/test5.h5', 'r')
# model = h5py.File('/home/dion/test4.h5', 'r')
@@ -1306,9 +1310,9 @@ class zzz(bpy.types.Operator):
collection.objects.link(obj)
bpy.context.scene.collection.children.link(collection)
print('Finished', time.time() - start)
print("Finished", time.time() - start)
newmem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Mem", newmem-mem)
print("Mem", newmem - mem)
return {"FINISHED"}
@@ -1326,13 +1330,14 @@ class zxc(bpy.types.Operator):
import time
from mathutils import Matrix
import resource
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
start = time.time()
self.collection = bpy.data.collections.new("Project")
model = h5py.File('/home/dion/test3.h5', 'r')
model = h5py.File("/home/dion/test3.h5", "r")
# model = h5py.File('/home/dion/test5.h5', 'r')
print('Opened', time.time() - start)
print("Opened", time.time() - start)
materials = {}
for i, rgb in enumerate(model["materials"]):
@@ -1340,7 +1345,7 @@ class zxc(bpy.types.Operator):
blender_mat.diffuse_color = rgb[()].tolist()
materials[i] = blender_mat
print('Materials', time.time() - start)
print("Materials", time.time() - start)
shapes = {}
for shape_id, shape in model["shapes"].items():
@@ -1354,7 +1359,7 @@ class zxc(bpy.types.Operator):
"material_ids": shape["material_ids"][()].tolist() if "material_ids" in shape else None,
}
print('Shapes', time.time() - start)
print("Shapes", time.time() - start)
chunk_size = 10000
@@ -1410,9 +1415,9 @@ class zxc(bpy.types.Operator):
self.create_object(chunked_verts, chunked_faces, chunked_materials, chunked_material_ids)
bpy.context.scene.collection.children.link(self.collection)
print('Finished', time.time() - start)
print("Finished", time.time() - start)
newmem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Mem", newmem-mem)
print("Mem", newmem - mem)
return {"FINISHED"}
def apply_matrix_to_flat_list(self, flat_list, matrix):
@@ -1470,6 +1475,7 @@ class aaa(bpy.types.Operator):
import time
from mathutils import Matrix
import resource
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
start = time.time()
@@ -1491,12 +1497,14 @@ class aaa(bpy.types.Operator):
# e.get_faces()
# self.create_object(e.verts, e.faces, e.get_materials(), e.get_material_ids()) # 14.7
# self.create_object(list(e.verts), list(e.faces), e.get_materials(), e.get_material_ids()) # 8.9
self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids()) # 6.5
o = self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids()) # 6.5
o["guids"] = [ifcopenshell.guid.compress(g) for g in list(e.guids)]
o["guid_ids"] = list(e.guid_ids)
bpy.context.scene.collection.children.link(self.collection)
print('Finished', time.time() - start)
print("Finished", time.time() - start)
newmem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Mem", newmem-mem)
print("Mem", newmem - mem)
return {"FINISHED"}
def create_object(self, verts, faces, materials, material_ids):
@@ -1529,6 +1537,8 @@ class aaa(bpy.types.Operator):
obj = bpy.data.objects.new("Blah", mesh)
self.collection.objects.link(obj)
return obj
class asdfasdf(bpy.types.Operator):
bl_idname = "bim.asdfasdf"
@@ -1543,18 +1553,19 @@ class asdfasdf(bpy.types.Operator):
import numpy as np
from mathutils import Matrix
import resource
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
start = time.time()
self.collection = bpy.data.collections.new("Project")
# ifc_file = ifcopenshell.open('/home/dion/test.ifc')
# ifc_file = ifcopenshell.open('/home/dion/drive/ifcs/racbasicsampleproject.ifc')
ifc_file = ifcopenshell.open("/home/dion/drive/ifcs/racbasicsampleproject.ifc")
# ifc_file = ifcopenshell.open('/home/dion/drive/ifcs/TXG_sample_project-fixed-IFC4.ifc')
ifc_file = ifcopenshell.open("/home/dion/tmp/petrubug/F-ELECT.ifc")
# ifc_file = ifcopenshell.open("/home/dion/tmp/petrubug/F-ELECT.ifc")
print("Finished opening")
settings = ifcopenshell.geom.settings()
settings.set_context_ids([ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW").id()])
# settings.set_context_ids([ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW").id()])
els = set(ifc_file.by_type("IfcElement"))
els |= set(ifc_file.by_type("IfcSite"))
els -= set(ifc_file.by_type("IfcFeatureElement"))
@@ -1569,7 +1580,9 @@ class asdfasdf(bpy.types.Operator):
ci = 0
if iterator.initialize():
while True:
has_processed_chunk = False
if iterator.process_chunk():
has_processed_chunk = True
ci += 1
print("Doing chunk", ci)
e = iterator.get_chunk()
@@ -1579,18 +1592,34 @@ class asdfasdf(bpy.types.Operator):
blender_mat.diffuse_color = list(c)
self.materials.append(blender_mat)
total_materials += 1
self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids())
o = self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids())
o["guids"] = list(e.guids)
o["guid_ids"] = list(e.guid_ids)
if not iterator.next():
if not has_processed_chunk:
# The left over chunk
e = iterator.get_chunk()
for c in e.colours:
blender_mat = bpy.data.materials.new(str(total_materials))
blender_mat.diffuse_color = list(c)
self.materials.append(blender_mat)
total_materials += 1
o = self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids())
o["guids"] = list(e.guids)
o["guid_ids"] = list(e.guid_ids)
break
bpy.context.scene.collection.children.link(self.collection)
print('Finished', time.time() - start)
print("Finished", time.time() - start)
newmem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
print("Mem", newmem-mem)
print("Mem", newmem - mem)
return {"FINISHED"}
def create_object(self, verts, faces, materials, material_ids):
num_vertices = len(verts) // 3
if not num_vertices:
return
total_faces = len(faces)
loop_start = range(0, total_faces, 3)
num_loops = total_faces // 3
@@ -1618,3 +1647,105 @@ class asdfasdf(bpy.types.Operator):
obj = bpy.data.objects.new("Blah", mesh)
self.collection.objects.link(obj)
return 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):
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
bpy.context.scene.BIMProjectProperties.queried_obj = None
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.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
import sqlite3
self.db = sqlite3.connect("/home/dion/Projects/ifcopenshell/src/ifcclash/foo.sqlite")
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]
sorted_properties = [(k, properties[k]) for k in sorted(properties.keys())]
LinksData.linked_data = {"attributes": attributes, "properties": sorted_properties}
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)
@@ -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))
@@ -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,23 @@ class BIM_PT_links(Panel):
"active_link_index",
)
if not LinksData.linked_data:
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)
class BIM_UL_library(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):