mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 05:52:33 +00:00
Type properties are now shown and direct linking of IFC (background blend generation) is now possible
This commit is contained in:
@@ -53,7 +53,7 @@ classes = (
|
|||||||
operator.zzz,
|
operator.zzz,
|
||||||
operator.zxc,
|
operator.zxc,
|
||||||
operator.aaa,
|
operator.aaa,
|
||||||
operator.asdfasdf,
|
operator.LoadLinkedProject,
|
||||||
operator.QueryLinkedElement,
|
operator.QueryLinkedElement,
|
||||||
prop.LibraryElement,
|
prop.LibraryElement,
|
||||||
prop.FilterCategory,
|
prop.FilterCategory,
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import gpu
|
||||||
|
import bmesh
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from bpy.types import SpaceView3D
|
||||||
|
from mathutils import Vector
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectDecorator:
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls, context):
|
||||||
|
if cls.installed:
|
||||||
|
cls.uninstall()
|
||||||
|
handler = cls()
|
||||||
|
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_VIEW")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def uninstall(cls):
|
||||||
|
try:
|
||||||
|
SpaceView3D.draw_handler_remove(cls.installed, "WINDOW")
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
cls.installed = None
|
||||||
|
|
||||||
|
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||||
|
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||||
|
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||||
|
shader.uniform_float("color", color)
|
||||||
|
batch.draw(shader)
|
||||||
|
|
||||||
|
def __call__(self, context):
|
||||||
|
self.addon_prefs = context.preferences.addons["blenderbim"].preferences
|
||||||
|
selected_elements_color = self.addon_prefs.decorator_color_selected
|
||||||
|
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
||||||
|
special_elements_color = self.addon_prefs.decorator_color_special
|
||||||
|
|
||||||
|
def transparent_color(color, alpha=0.1):
|
||||||
|
color = [i for i in color]
|
||||||
|
color[3] = alpha
|
||||||
|
return color
|
||||||
|
|
||||||
|
gpu.state.point_size_set(6)
|
||||||
|
gpu.state.blend_set("ALPHA")
|
||||||
|
|
||||||
|
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||||
|
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||||
|
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||||
|
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||||
|
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||||
|
|
||||||
|
# general shader
|
||||||
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
|
|
||||||
|
selected_vertices = []
|
||||||
|
selected_edges = []
|
||||||
|
selected_tris = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj = context.scene.BIMProjectProperties.queried_obj
|
||||||
|
selected_vertices = obj["selected_vertices"]
|
||||||
|
selected_edges = obj["selected_edges"]
|
||||||
|
selected_tris = obj["selected_tris"]
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected_edges:
|
||||||
|
self.draw_batch("LINES", selected_vertices, selected_elements_color, selected_edges)
|
||||||
|
self.draw_batch("TRIS", selected_vertices, transparent_color(selected_elements_color), selected_tris)
|
||||||
@@ -21,6 +21,7 @@ import bpy
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import subprocess
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
@@ -814,16 +815,17 @@ class ToggleFilterCategories(bpy.types.Operator):
|
|||||||
|
|
||||||
class LinkIfc(bpy.types.Operator):
|
class LinkIfc(bpy.types.Operator):
|
||||||
bl_idname = "bim.link_ifc"
|
bl_idname = "bim.link_ifc"
|
||||||
bl_label = "Link Blend/IFC File"
|
bl_label = "Link IFC"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "This will link the Blender file that is synced with the IFC file"
|
bl_description = "Reference in a read-only IFC model in the background"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
files: bpy.props.CollectionProperty(name="Files", type=bpy.types.OperatorFileListElement)
|
files: bpy.props.CollectionProperty(name="Files", type=bpy.types.OperatorFileListElement)
|
||||||
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
||||||
filter_glob: bpy.props.StringProperty(default="*.blend;*.blend1", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
start = time.time()
|
||||||
files = [f.name for f in self.files] if self.files else [self.filepath]
|
files = [f.name for f in self.files] if self.files else [self.filepath]
|
||||||
for filename in files:
|
for filename in files:
|
||||||
filepath = os.path.join(self.directory, filename)
|
filepath = os.path.join(self.directory, filename)
|
||||||
@@ -835,6 +837,7 @@ class LinkIfc(bpy.types.Operator):
|
|||||||
filepath = os.path.relpath(filepath, bpy.path.abspath("//"))
|
filepath = os.path.relpath(filepath, bpy.path.abspath("//"))
|
||||||
new.name = filepath
|
new.name = filepath
|
||||||
bpy.ops.bim.load_link(filepath=filepath)
|
bpy.ops.bim.load_link(filepath=filepath)
|
||||||
|
print(f"Finished linking {len(files)} IFCs", time.time() - start)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -890,6 +893,13 @@ class LoadLink(bpy.types.Operator):
|
|||||||
filepath = self.filepath
|
filepath = self.filepath
|
||||||
if not os.path.isabs(filepath):
|
if not os.path.isabs(filepath):
|
||||||
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
|
filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), filepath))
|
||||||
|
if self.filepath.lower().endswith(".blend"):
|
||||||
|
self.link_blend(filepath)
|
||||||
|
elif self.filepath.lower().endswith(".ifc"):
|
||||||
|
self.link_ifc()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def link_blend(self, filepath):
|
||||||
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
|
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
|
||||||
data_to.scenes = data_from.scenes
|
data_to.scenes = data_from.scenes
|
||||||
for scene in bpy.data.scenes:
|
for scene in bpy.data.scenes:
|
||||||
@@ -899,9 +909,36 @@ class LoadLink(bpy.types.Operator):
|
|||||||
if "IfcProject" not in child.name:
|
if "IfcProject" not in child.name:
|
||||||
continue
|
continue
|
||||||
bpy.data.scenes[0].collection.children.link(child)
|
bpy.data.scenes[0].collection.children.link(child)
|
||||||
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
link = bpy.context.scene.BIMProjectProperties.links.get(self.filepath)
|
||||||
link.is_loaded = True
|
link.is_loaded = True
|
||||||
return {"FINISHED"}
|
|
||||||
|
def link_ifc(self):
|
||||||
|
blend_filepath = self.filepath + ".cache.blend"
|
||||||
|
h5_filepath = self.filepath + ".cache.h5"
|
||||||
|
|
||||||
|
if not os.path.exists(blend_filepath):
|
||||||
|
code = f"""
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
def run():
|
||||||
|
bpy.ops.bim.load_linked_project(filepath="{self.filepath}")
|
||||||
|
bpy.ops.wm.save_as_mainfile(filepath="{blend_filepath}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
run()
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
exit(1)
|
||||||
|
"""
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as temp_file:
|
||||||
|
temp_file.write(code)
|
||||||
|
run = subprocess.run([bpy.app.binary_path, "-b", "--python", temp_file.name, "--python-exit-code", "1"])
|
||||||
|
if run.returncode == 1:
|
||||||
|
print("An error occurred while processing your IFC.")
|
||||||
|
|
||||||
|
self.link_blend(blend_filepath)
|
||||||
|
|
||||||
|
|
||||||
class ReloadLink(bpy.types.Operator):
|
class ReloadLink(bpy.types.Operator):
|
||||||
@@ -1143,7 +1180,6 @@ class xxx(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import time
|
import time
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import ifcopenshell
|
|
||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from mathutils import Matrix
|
from mathutils import Matrix
|
||||||
@@ -1540,83 +1576,99 @@ class aaa(bpy.types.Operator):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class asdfasdf(bpy.types.Operator):
|
class LoadLinkedProject(bpy.types.Operator):
|
||||||
bl_idname = "bim.asdfasdf"
|
bl_idname = "bim.load_linked_project"
|
||||||
bl_label = "Regular IFC iterator with C++ chunking"
|
bl_label = "Load a project for viewing only."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
filepath: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import time
|
import ifcpatch
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import ifcopenshell
|
|
||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import numpy as np
|
|
||||||
from mathutils import Matrix
|
|
||||||
import resource
|
|
||||||
|
|
||||||
mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
self.collection = bpy.data.collections.new("Project")
|
self.filepath = self.filepath or "/home/dion/drive/ifcs/racbasicsampleproject.ifc"
|
||||||
# ifc_file = ifcopenshell.open('/home/dion/test.ifc')
|
|
||||||
ifc_file = ifcopenshell.open("/home/dion/drive/ifcs/racbasicsampleproject.ifc")
|
self.collection = bpy.data.collections.new("IfcProject/" + os.path.basename(self.filepath))
|
||||||
# ifc_file = ifcopenshell.open('/home/dion/drive/ifcs/TXG_sample_project-fixed-IFC4.ifc')
|
self.file = ifcopenshell.open(self.filepath)
|
||||||
# ifc_file = ifcopenshell.open("/home/dion/tmp/petrubug/F-ELECT.ifc")
|
# self.file = ifcopenshell.open('/home/dion/test.ifc')
|
||||||
|
# self.file = ifcopenshell.open('/home/dion/drive/ifcs/TXG_sample_project-fixed-IFC4.ifc')
|
||||||
|
# self.file = ifcopenshell.open("/home/dion/tmp/petrubug/F-ELECT.ifc")
|
||||||
print("Finished opening")
|
print("Finished opening")
|
||||||
|
|
||||||
settings = ifcopenshell.geom.settings()
|
self.db_filepath = self.filepath + ".cache.sqlite"
|
||||||
# settings.set_context_ids([ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW").id()])
|
db = ifcpatch.execute(
|
||||||
els = set(ifc_file.by_type("IfcElement"))
|
{"input": self.filepath, "file": self.file, "recipe": "ExtractPropertiesToSQLite", "arguments": []}
|
||||||
els |= set(ifc_file.by_type("IfcSite"))
|
)
|
||||||
els -= set(ifc_file.by_type("IfcFeatureElement"))
|
ifcpatch.write(db, self.db_filepath)
|
||||||
els = list(els)
|
print("Finished writing property database")
|
||||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=els)
|
|
||||||
meshes = {}
|
|
||||||
blender_mats = {}
|
|
||||||
|
|
||||||
total_materials = 0
|
logger = logging.getLogger("ImportIFC")
|
||||||
self.materials = []
|
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||||
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||||
|
ifc_importer.file = self.file
|
||||||
|
ifc_importer.process_context_filter()
|
||||||
|
|
||||||
ci = 0
|
self.elements = set(self.file.by_type("IfcElement"))
|
||||||
if iterator.initialize():
|
if self.file.schema in ("IFC2X3", "IFC4"):
|
||||||
while True:
|
self.elements |= set(self.file.by_type("IfcProxy"))
|
||||||
has_processed_chunk = False
|
self.elements |= set(self.file.by_type("IfcSite"))
|
||||||
if iterator.process_chunk():
|
self.elements -= set(self.file.by_type("IfcFeatureElement"))
|
||||||
has_processed_chunk = True
|
self.elements = list(self.elements)
|
||||||
ci += 1
|
|
||||||
print("Doing chunk", ci)
|
|
||||||
e = iterator.get_chunk()
|
|
||||||
|
|
||||||
for c in e.colours:
|
for settings in ifc_importer.context_settings:
|
||||||
blender_mat = bpy.data.materials.new(str(total_materials))
|
iterator = ifcopenshell.geom.iterator(
|
||||||
blender_mat.diffuse_color = list(c)
|
settings, self.file, multiprocessing.cpu_count(), include=self.elements
|
||||||
self.materials.append(blender_mat)
|
)
|
||||||
total_materials += 1
|
meshes = {}
|
||||||
o = self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids())
|
blender_mats = {}
|
||||||
o["guids"] = list(e.guids)
|
|
||||||
o["guid_ids"] = list(e.guid_ids)
|
total_materials = 0
|
||||||
if not iterator.next():
|
self.materials = []
|
||||||
if not has_processed_chunk:
|
|
||||||
# The left over chunk
|
ci = 0
|
||||||
e = iterator.get_chunk()
|
if iterator.initialize():
|
||||||
for c in e.colours:
|
while True:
|
||||||
|
has_processed_chunk = False
|
||||||
|
if iterator.process_chunk():
|
||||||
|
has_processed_chunk = True
|
||||||
|
ci += 1
|
||||||
|
if ci % 50 == 0:
|
||||||
|
print("Doing chunk", ci)
|
||||||
|
chunk = iterator.get_chunk()
|
||||||
|
|
||||||
|
for colour in chunk.colours:
|
||||||
blender_mat = bpy.data.materials.new(str(total_materials))
|
blender_mat = bpy.data.materials.new(str(total_materials))
|
||||||
blender_mat.diffuse_color = list(c)
|
blender_mat.diffuse_color = list(colour)
|
||||||
self.materials.append(blender_mat)
|
self.materials.append(blender_mat)
|
||||||
total_materials += 1
|
total_materials += 1
|
||||||
|
self.create_object(chunk)
|
||||||
|
|
||||||
o = self.create_object(e.get_verts(), e.get_faces(), e.get_materials(), e.get_material_ids())
|
if not iterator.next():
|
||||||
o["guids"] = list(e.guids)
|
if not has_processed_chunk:
|
||||||
o["guid_ids"] = list(e.guid_ids)
|
# The left over chunk
|
||||||
break
|
chunk = iterator.get_chunk()
|
||||||
|
for colour in chunk.colours:
|
||||||
|
blender_mat = bpy.data.materials.new(str(total_materials))
|
||||||
|
blender_mat.diffuse_color = list(colour)
|
||||||
|
self.materials.append(blender_mat)
|
||||||
|
total_materials += 1
|
||||||
|
self.create_object(chunk)
|
||||||
|
break
|
||||||
|
|
||||||
bpy.context.scene.collection.children.link(self.collection)
|
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
|
break
|
||||||
print("Mem", newmem - mem)
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def create_object(self, verts, faces, materials, material_ids):
|
def create_object(self, chunk):
|
||||||
|
verts = chunk.get_verts()
|
||||||
|
faces = chunk.get_faces()
|
||||||
|
materials = chunk.get_materials()
|
||||||
|
material_ids = chunk.get_material_ids()
|
||||||
|
|
||||||
num_vertices = len(verts) // 3
|
num_vertices = len(verts) // 3
|
||||||
if not num_vertices:
|
if not num_vertices:
|
||||||
return
|
return
|
||||||
@@ -1645,9 +1697,12 @@ class asdfasdf(bpy.types.Operator):
|
|||||||
|
|
||||||
mesh.update()
|
mesh.update()
|
||||||
|
|
||||||
obj = bpy.data.objects.new("Blah", mesh)
|
obj = bpy.data.objects.new("Chunk", mesh)
|
||||||
|
obj["guids"] = list(chunk.guids)
|
||||||
|
obj["guid_ids"] = list(chunk.guid_ids)
|
||||||
|
obj["db"] = self.db_filepath
|
||||||
|
|
||||||
self.collection.objects.link(obj)
|
self.collection.objects.link(obj)
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class QueryLinkedElement(bpy.types.Operator):
|
class QueryLinkedElement(bpy.types.Operator):
|
||||||
@@ -1660,10 +1715,20 @@ class QueryLinkedElement(bpy.types.Operator):
|
|||||||
return context.area.type == "VIEW_3D"
|
return context.area.type == "VIEW_3D"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
import sqlite3
|
||||||
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
|
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
|
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
|
region = context.region
|
||||||
rv3d = context.region_data
|
rv3d = context.region_data
|
||||||
coord = (self.mouse_x, self.mouse_y)
|
coord = (self.mouse_x, self.mouse_y)
|
||||||
@@ -1704,9 +1769,7 @@ class QueryLinkedElement(bpy.types.Operator):
|
|||||||
break
|
break
|
||||||
guid_start_index = guid_end_index
|
guid_start_index = guid_end_index
|
||||||
|
|
||||||
import sqlite3
|
self.db = sqlite3.connect(obj["db"])
|
||||||
|
|
||||||
self.db = sqlite3.connect("/home/dion/Projects/ifcopenshell/src/ifcclash/foo.sqlite")
|
|
||||||
self.c = self.db.cursor()
|
self.c = self.db.cursor()
|
||||||
|
|
||||||
self.c.execute(f"SELECT * FROM elements WHERE global_id = '{guid}' LIMIT 1")
|
self.c.execute(f"SELECT * FROM elements WHERE global_id = '{guid}' LIMIT 1")
|
||||||
@@ -1724,9 +1787,27 @@ class QueryLinkedElement(bpy.types.Operator):
|
|||||||
for row in rows:
|
for row in rows:
|
||||||
properties.setdefault(row[1], {})[row[2]] = row[3]
|
properties.setdefault(row[1], {})[row[2]] = row[3]
|
||||||
|
|
||||||
sorted_properties = [(k, properties[k]) for k in sorted(properties.keys())]
|
self.c.execute("SELECT * FROM relationships WHERE from_id = ?", (element[0],))
|
||||||
|
relationships = self.c.fetchall()
|
||||||
|
|
||||||
LinksData.linked_data = {"attributes": attributes, "properties": sorted_properties}
|
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:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == "PROPERTIES":
|
if area.type == "PROPERTIES":
|
||||||
|
|||||||
@@ -324,6 +324,18 @@ class BIM_PT_links(Panel):
|
|||||||
row.label(text=name)
|
row.label(text=name)
|
||||||
row.label(text=value)
|
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):
|
class BIM_UL_library(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):
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy.types import WorkSpaceTool
|
||||||
|
|
||||||
|
|
||||||
|
class QueryTool(bpy.types.WorkSpaceTool):
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_context_mode = 'OBJECT'
|
||||||
|
bl_idname = "bim.query_tool"
|
||||||
|
bl_label = "Query Tool"
|
||||||
|
bl_description = "Fetch data about a linked IFC element"
|
||||||
|
bl_icon = "ops.generic.select_circle"
|
||||||
|
bl_widget = None
|
||||||
|
bl_keymap = (
|
||||||
|
("bim.query_linked_element", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||||
|
)
|
||||||
@@ -117,8 +117,12 @@ class Patcher:
|
|||||||
psets = ifcopenshell.util.element.get_psets(element, should_inherit=False)
|
psets = ifcopenshell.util.element.get_psets(element, should_inherit=False)
|
||||||
for pset_name, pset_data in psets.items():
|
for pset_name, pset_data in psets.items():
|
||||||
for prop_name, value in pset_data.items():
|
for prop_name, value in pset_data.items():
|
||||||
if prop_name == "id":
|
if prop_name == "id" or value is None or value == "":
|
||||||
continue
|
continue
|
||||||
|
if isinstance(value, bool):
|
||||||
|
value = "True" if value else "False"
|
||||||
|
elif not isinstance(value, str):
|
||||||
|
value = str(value)
|
||||||
properties.append([i, pset_name, prop_name, value])
|
properties.append([i, pset_name, prop_name, value])
|
||||||
relating_type = ifcopenshell.util.element.get_type(element)
|
relating_type = ifcopenshell.util.element.get_type(element)
|
||||||
if relating_type and relating_type != element:
|
if relating_type and relating_type != element:
|
||||||
|
|||||||
Reference in New Issue
Block a user