This commit is contained in:
Andrej730
2026-03-03 14:34:05 +05:00
parent 619848823c
commit 8743d5643e
7 changed files with 70 additions and 36 deletions
+1 -1
View File
@@ -170,6 +170,6 @@ class ProjectLibraryData:
class LinksData:
linked_data = {}
linked_data: dict[str, Any] = {}
enable_culling = False
is_loaded = False
@@ -16,8 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
import bmesh
import bpy
import gpu
@@ -54,7 +52,7 @@ class ProjectDecorator:
installed = None
@classmethod
def install(cls, context):
def install(cls, context: bpy.types.Context) -> None:
if cls.installed:
cls.uninstall()
handler = cls()
@@ -99,9 +97,9 @@ class ProjectDecorator:
# general shader
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
selected_vertices = []
selected_edges = []
selected_tris = []
selected_vertices: list[tuple[float, float, float]] = []
selected_edges: list[tuple[int, int]] = []
selected_tris: list[tuple[int, int, int]] = []
props = tool.Project.get_project_props()
try:
@@ -112,7 +110,7 @@ class ProjectDecorator:
except:
return
root_obj: Union[bpy.types.Object, None] = props.queried_obj_root
root_obj = props.queried_obj_root
if root_obj and not (m := root_obj.matrix_world).is_identity:
selected_vertices = [m @ Vector(v) for v in selected_vertices]
@@ -27,11 +27,12 @@ import traceback
from collections import defaultdict
from math import radians
from pathlib import Path
from typing import TYPE_CHECKING, Literal, Union, get_args
from typing import TYPE_CHECKING, Any, Literal, Union, get_args
import bpy
import ifcopenshell
import ifcopenshell.api.attribute
import ifcopenshell.api.document
import ifcopenshell.api.nest
import ifcopenshell.api.project
import ifcopenshell.api.root
@@ -1438,8 +1439,13 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Load Link"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Load the selected file"
link_index: bpy.props.IntProperty(name="Link Index")
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
link_index: int
use_cache: bool
def _execute(self, context):
self.link = tool.Project.get_project_props().links[self.link_index]
@@ -1464,9 +1470,10 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
empty = bpy.data.objects.new(empty_name, None)
empty.instance_type = "COLLECTION"
empty.instance_collection = collection
empty.matrix_world = Matrix(tool.Project.calculate_link_matrix(self.link))
empty.matrix_world = tool.Project.calculate_link_matrix(self.link)
tool.Project.set_link_empty_handle(self.link, empty)
assert bpy.context.scene
bpy.context.scene.collection.objects.link(empty)
self.link.is_loaded = True
if tool.Ifc.get(): # For non-IFC projects, locking has no meaning
@@ -1635,8 +1642,16 @@ class ToggleLinkVisibility(bpy.types.Operator):
bl_label = "Toggle Link Visibility"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Toggle visibility between SOLID and WIREFRAME"
link_index: bpy.props.IntProperty(name="Link Index")
mode: bpy.props.EnumProperty(name="Visibility Mode", items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")))
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
mode: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
name="Visibility Mode",
items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")),
)
if TYPE_CHECKING:
link_index: int
mode: Literal["WIREFRAME", "VISIBLE"]
def execute(self, context):
props = tool.Project.get_project_props()
@@ -1688,8 +1703,11 @@ class EnableEditingLink(bpy.types.Operator):
def execute(self, context):
link = tool.Project.get_project_props().active_link
assert link
link.is_editing = True
tool.Geometry.unlock_object(tool.Project.get_link_empty_handle(link))
obj = tool.Project.get_link_empty_handle(link)
assert obj
tool.Geometry.unlock_object(obj)
return {"FINISHED"}
@@ -1701,9 +1719,11 @@ class DisableEditingLink(bpy.types.Operator):
def execute(self, context):
link = tool.Project.get_project_props().active_link
assert link
link.is_editing = False
obj = tool.Project.get_link_empty_handle(link)
obj.matrix_world = Matrix(tool.Project.calculate_link_matrix(link))
assert obj
obj.matrix_world = tool.Project.calculate_link_matrix(link)
tool.Geometry.lock_object(obj)
return {"FINISHED"}
@@ -1716,8 +1736,10 @@ class EditLink(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
link = tool.Project.get_project_props().active_link
assert link
link.is_editing = False
obj = tool.Project.get_link_empty_handle(link)
assert obj
new_obj_matrix = obj.matrix_world
filepath = Path(tool.Ifc.resolve_uri(link.filepath))
@@ -1753,7 +1775,7 @@ class EditLink(bpy.types.Operator, tool.Ifc.Operator):
else:
link.transformation = transformation
obj.matrix_world = Matrix(tool.Project.calculate_link_matrix(link))
obj.matrix_world = tool.Project.calculate_link_matrix(link)
tool.Geometry.lock_object(obj)
@@ -2270,7 +2292,8 @@ class QueryLinkedElement(bpy.types.Operator):
props = tool.Project.get_project_props()
props.queried_obj = None
for area in bpy.context.screen.areas:
assert context.screen
for area in context.screen.areas:
if area.type == "PROPERTIES":
for region in area.regions:
if region.type == "WINDOW":
@@ -2278,6 +2301,7 @@ class QueryLinkedElement(bpy.types.Operator):
elif area.type == "VIEW_3D":
area.tag_redraw()
assert context.region and context.region_data
region = context.region
rv3d = context.region_data
coord = (self.mouse_x, self.mouse_y)
@@ -2294,18 +2318,20 @@ class QueryLinkedElement(bpy.types.Operator):
guid = None
guid_start_index = 0
for i, guid_end_index in enumerate(obj["guid_ids"]):
guid_ids: list[int] = obj["guid_ids"]
for i, guid_end_index in enumerate(guid_ids):
if face_index < guid_end_index:
guid = obj["guids"][i]
props.queried_obj = obj
props.queried_obj_root = self.find_obj_root(obj, instance_matrix)
selected_tris = []
selected_edges = []
vert_indices = set()
selected_tris: list[tuple[int, ...]] = []
selected_edges: list[tuple[int, ...]] = []
vert_indices_set: set[int] = set()
assert isinstance(obj.data, bpy.types.Mesh)
for polygon in obj.data.polygons[guid_start_index:guid_end_index]:
vert_indices.update(polygon.vertices)
vert_indices = list(vert_indices)
vert_indices_set.update(polygon.vertices)
vert_indices = list(vert_indices_set)
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]:
@@ -2319,13 +2345,14 @@ class QueryLinkedElement(bpy.types.Operator):
break
guid_start_index = guid_end_index
assert guid is not None
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 = {}
attributes: dict[str, Any] = {}
for i, attr in enumerate(["GlobalId", "IFC Class", "Predefined Type", "Name", "Description"]):
if element[i + 1] is not None:
attributes[attr] = element[i + 1]
@@ -2415,6 +2442,7 @@ class AppendInspectedLinkedElement(AppendLibraryElement):
return {"CANCELLED"}
queried_obj = props.queried_obj
assert queried_obj
ifc_file = tool.Ifc.get()
linked_ifc_file: ifcopenshell.file
@@ -2683,10 +2711,12 @@ class CreateClippingPlane(bpy.types.Operator):
self.report({"INFO"}, "Maximum of six clipping planes allowed.")
return {"FINISHED"}
assert context.screen
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
assert context.region and context.region_data
region = context.region
rv3d = context.region_data
if rv3d: # Called from a 3D viewport
+2
View File
@@ -774,6 +774,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
bsdd_load_preview_dictionaries: bool
bsdd_load_inactive_dictionaries: bool
bsdd_load_test_dictionaries: bool
bsdd_baseurl: str
should_disable_undo_on_save: bool
should_stream: bool
should_always_cache: bool
@@ -789,6 +790,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
mass_time_units_in_wizard: bool
chain_filter_with_set_operations: bool
save_metadata_blend_file: bool
metadata_blend_file_suffix: str
decorator_font_scale: float
def draw(self, context: bpy.types.Context) -> None:
+6 -5
View File
@@ -31,6 +31,7 @@ import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
import numpy as np
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
@@ -45,7 +46,7 @@ import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
if TYPE_CHECKING:
from bonsai.bim.module.project.prop import BIMProjectProperties, MeasureToolSettings
from bonsai.bim.module.project.prop import BIMProjectProperties, MeasureToolSettings, Link
HiearchyDict = dict[ifcopenshell.entity_instance, "HiearchyDict"]
@@ -61,20 +62,20 @@ class Project(bonsai.core.tool.Project):
return scene.MeasureToolSettings # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_link_empty_handle(cls, link) -> bpy.types.Object | None:
def get_link_empty_handle(cls, link: Link) -> bpy.types.Object | None:
if tool.Ifc.get():
return tool.Ifc.get_object(tool.Ifc.get().by_id(link.ifc_definition_id))
return link.empty_handle
@classmethod
def set_link_empty_handle(cls, link, empty: bpy.types.Object) -> None:
def set_link_empty_handle(cls, link: Link, empty: bpy.types.Object) -> None:
if tool.Ifc.get():
tool.Ifc.link(tool.Ifc.get().by_id(link.ifc_definition_id), empty)
else:
link.empty_handle = empty
@classmethod
def calculate_link_matrix(cls, link) -> None:
def calculate_link_matrix(cls, link: Link) -> Matrix:
filepath = Path(tool.Ifc.resolve_uri(link.filepath))
with open(filepath.with_suffix(".ifc.cache.json"), "r") as f:
metadata = json.load(f)
@@ -99,7 +100,7 @@ class Project(bonsai.core.tool.Project):
rot = ifcopenshell.util.shape_builder.np_rotation_matrix(radians(-float(gprops.model_project_north)), 4, "Z")
local_matrix = rot @ np.eye(4)
local_matrix[:, 3][:3] = [float(o) for o in gprops.model_origin_si.split(",")]
return np.linalg.inv(local_matrix) @ global_matrix
return Matrix(np.linalg.inv(local_matrix) @ global_matrix)
@classmethod
def append_all_types_from_template(cls, template: str) -> None:
@@ -17,21 +17,24 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import logging
import tempfile
import ifcopenshell.util.element
import ifcpatch
try:
import sqlite3
except:
print("No SQLite support")
class Patcher:
class Patcher(ifcpatch.BasePatcher):
def __init__(
self,
file,
logger,
file: ifcopenshell.file,
logger: logging.Logger | None = None,
):
"""Extracts properties and relationships from a IFC-SPF model to SQLite.
@@ -45,10 +48,11 @@ class Patcher:
result = ifcpatch.execute({"input": fn, "file": model, "recipe": "ExtractPropertiesToSQLite"})
ifcpatch.write(result, "output.sqlite")
"""
self.file = file
self.logger = logger
super().__init__(file, logger)
def patch(self):
import sqlite3
tmp = tempfile.NamedTemporaryFile(delete=False)
db_file = tmp.name
self.db = sqlite3.connect(db_file)
-1
View File
@@ -125,7 +125,6 @@ class Patcher(ifcpatch.BasePatcher):
)
"""
super().__init__(file, logger)
self.logger = logger
self.sql_type: Literal["sqlite", "mysql"] = sql_type.lower()
self.host = host
self.username = username