From 1193e7025abfb2da968fb2212b5741ad12d68625 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 26 Sep 2023 14:53:54 +0500 Subject: [PATCH] system decorations - live time update Decoration data is now updating in live time, caching some parts in Data for performance. Real time update is needed because Data doesn't allow update on objects positions change and later will need real time update for viewport orientation data. --- .../bim/module/drawing/decoration.py | 11 +++-- .../blenderbim/bim/module/system/data.py | 43 +++++++++++++++---- .../blenderbim/bim/module/system/decorator.py | 8 ++-- src/blenderbim/blenderbim/tool/model.py | 9 +++- src/blenderbim/blenderbim/tool/system.py | 33 +++++++------- 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py index ebff8b188d..2c0bf10b1f 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py @@ -52,15 +52,20 @@ class profile_consequential: cls.test_name = test_name @classmethod - def log(cls): + def log(cls, args=[]): if cls.start_time is not None: - cls.lines.append(f"{cls.test_name}\t{timer() - cls.start_time:.10f}") + args = "" if not args else "\t" + "\t".join(args) + cls.lines.append(f"{cls.test_name}\t{timer() - cls.start_time:.10f}{args}") @classmethod def stop(cls): cls.log() cls.start_time = None - print("\n".join(cls.lines)) + lines = "\n".join(cls.lines) + print(lines) + import pyperclip + + pyperclip.copy(lines) cls.lines = [] diff --git a/src/blenderbim/blenderbim/bim/module/system/data.py b/src/blenderbim/blenderbim/bim/module/system/data.py index 3ba63ae1f7..6955fcdbbf 100644 --- a/src/blenderbim/blenderbim/bim/module/system/data.py +++ b/src/blenderbim/blenderbim/bim/module/system/data.py @@ -68,16 +68,18 @@ class ObjectSystemData: cls.data = { "systems": cls.systems(), "total_systems": cls.total_systems(), + # AFTER SYSTEMS + "connected_elements": cls.connected_elements(), } cls.is_loaded = True @classmethod def systems(cls): results = [] - element = tool.Ifc.get_entity(bpy.context.active_object) - if not element: + cls.element = tool.Ifc.get_entity(bpy.context.active_object) + if not cls.element: return results - for system in ifcopenshell.util.system.get_element_systems(element): + for system in ifcopenshell.util.system.get_element_systems(cls.element): results.append({"id": system.id(), "name": system.Name or "Unnamed", "ifc_class": system.is_a()}) return results @@ -85,6 +87,12 @@ class ObjectSystemData: def total_systems(cls): return len(tool.Ifc.get().by_type("IfcSystem")) + @classmethod + def connected_elements(cls): + if not cls.element: + return set() + return tool.System.get_connected_elements(cls.element) + class PortData: data = {} @@ -151,15 +159,34 @@ class PortData: class SystemDecorationData: data = {} + elements_ports_positions = {} is_loaded = False @classmethod def load(cls): - cls.data = { - "decoration_data": cls.decoration_data(), - } + cls.data = {} cls.is_loaded = True + cls.elements_ports_positions = {} @classmethod - def decoration_data(cls): - return tool.System.get_decoration_data() + def get_element_ports_data(cls, element): + """returns element's port data, caches the data until UI update + + Port data includes: + - local port position in SI units + - port flow direction + + """ + if element not in cls.elements_ports_positions: + ports = tool.System.get_ports(element) + si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + ports_data = [] + for port in ports: + position = tool.Model.get_element_matrix(port, keep_local=True).translation * si_conversion + port_data = { + "position": position, + "flow_direction": port.FlowDirection, + } + ports_data.append(port_data) + cls.elements_ports_positions[element] = ports_data + return cls.elements_ports_positions[element] diff --git a/src/blenderbim/blenderbim/bim/module/system/decorator.py b/src/blenderbim/blenderbim/bim/module/system/decorator.py index 8cebbae4f3..e3e8518f79 100644 --- a/src/blenderbim/blenderbim/bim/module/system/decorator.py +++ b/src/blenderbim/blenderbim/bim/module/system/decorator.py @@ -111,11 +111,9 @@ class SystemDecorator: roof_angle_edges = [] preview_edges = [] - # NOTE: using .data here doesn't allow having live viewport orientation for the calculations - # but not using .data seems to be performance heavy - if not SystemDecorationData.is_loaded: - SystemDecorationData.load() - decoration_data = SystemDecorationData.data["decoration_data"] + # NOTE: using live update because Data wouldn't allow + # live time update of objects positions + decoration_data = tool.System.get_decoration_data() all_vertices = decoration_data["all_vertices"] preview_edges = decoration_data["preview_edges"] diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index e2a2c29773..47ae276c3a 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -31,6 +31,7 @@ from blenderbim.bim.module.geometry.helper import Helper from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData import collections import json +import numpy as np class Model(blenderbim.core.tool.Model): @@ -815,8 +816,12 @@ class Model(blenderbim.core.tool.Model): tool.Ifc.run("geometry.edit_object_placement", product=element, matrix=matrix, is_si=True) @classmethod - def get_element_matrix(cls, element): - placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) + def get_element_matrix(cls, element, keep_local=False): + placement = element.ObjectPlacement + if keep_local: + placement = ifcopenshell.util.placement.get_axis2placement(placement.RelativePlacement) + else: + placement = ifcopenshell.util.placement.get_local_placement(placement) return Matrix(placement) @classmethod diff --git a/src/blenderbim/blenderbim/tool/system.py b/src/blenderbim/blenderbim/tool/system.py index 9688875b89..6a0cd8b080 100644 --- a/src/blenderbim/blenderbim/tool/system.py +++ b/src/blenderbim/blenderbim/tool/system.py @@ -24,6 +24,8 @@ from blenderbim.bim import import_ifc import re from math import pi, cos, sin from mathutils import Matrix, Vector +from blenderbim.bim.module.system.data import ObjectSystemData, SystemDecorationData +from blenderbim.bim.module.drawing.decoration import profile_consequential class System(blenderbim.core.tool.System): @@ -204,13 +206,16 @@ class System(blenderbim.core.tool.System): def most_aligned_vector(a, vectors): return max(vectors, key=lambda v: abs(a.dot(v))) - si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) start_vert_i = 0 - if bpy.context.active_object and (active_element := tool.Ifc.get_entity(bpy.context.active_object)): - selected_elements = cls.get_connected_elements(active_element) - else: - selected_elements = set() + if not ObjectSystemData.is_loaded: + ObjectSystemData.load() + + if not SystemDecorationData.is_loaded: + SystemDecorationData.load() + + object_system_data = ObjectSystemData.data + selected_elements = object_system_data["connected_elements"] # TODO: get only objects visible in viewport objects = set(bpy.data.objects) - set(bpy.data.collections["Types"].objects) @@ -230,19 +235,17 @@ class System(blenderbim.core.tool.System): continue selected_element = element in selected_elements - ports = tool.System.get_ports(element) verts_pos = [] - for port in ports: - position = tool.Model.get_element_matrix(port).translation * si_conversion - verts_pos.append(position) + port_data = SystemDecorationData.get_element_ports_data(element) + verts_pos.extend([obj.matrix_world @ data["position"] for data in port_data]) - verts = range(start_vert_i, start_vert_i + len(ports)) - edges = [(i, i + 1) for i in range(start_vert_i, start_vert_i + len(ports) - 1)] + verts = range(start_vert_i, start_vert_i + len(port_data)) + edges = [(i, i + 1) for i in range(start_vert_i, start_vert_i + len(port_data) - 1)] - def get_flow_direction(ports): + def get_flow_direction(port_data): # diagram - https://i.imgur.com/ioYL7bZ.png - flow_dirs = [p.FlowDirection for p in ports] + flow_dirs = [p["flow_direction"] for p in port_data] unique = set(flow_dirs) if len(unique) == 1: return 0 @@ -256,7 +259,7 @@ class System(blenderbim.core.tool.System): return -1 return 0 - if len(ports) == 2 and selected_element and (flow_direction := get_flow_direction(ports)): + if len(port_data) == 2 and selected_element and (flow_direction := get_flow_direction(port_data)): edge_verts = verts_pos.copy() edge_verts = edge_verts[::flow_direction] @@ -284,7 +287,7 @@ class System(blenderbim.core.tool.System): n_direction_lines = int(n_direction_lines) + 1 start_offset /= 2 start_offset = edge_dir * start_offset + base_vert - cur_vert_index = start_vert_i + len(ports) + cur_vert_index = start_vert_i + len(port_data) for i in range(n_direction_lines): cur_offset = start_offset + edge_dir * i * direction_lines_offset