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.
This commit is contained in:
Andrej730
2023-09-26 14:53:54 +05:00
parent ae483f7aca
commit ac18bba661
5 changed files with 71 additions and 33 deletions
@@ -52,15 +52,20 @@ class profile_consequential:
cls.test_name = test_name cls.test_name = test_name
@classmethod @classmethod
def log(cls): def log(cls, args=[]):
if cls.start_time is not None: 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 @classmethod
def stop(cls): def stop(cls):
cls.log() cls.log()
cls.start_time = None cls.start_time = None
print("\n".join(cls.lines)) lines = "\n".join(cls.lines)
print(lines)
import pyperclip
pyperclip.copy(lines)
cls.lines = [] cls.lines = []
@@ -68,16 +68,18 @@ class ObjectSystemData:
cls.data = { cls.data = {
"systems": cls.systems(), "systems": cls.systems(),
"total_systems": cls.total_systems(), "total_systems": cls.total_systems(),
# AFTER SYSTEMS
"connected_elements": cls.connected_elements(),
} }
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
def systems(cls): def systems(cls):
results = [] results = []
element = tool.Ifc.get_entity(bpy.context.active_object) cls.element = tool.Ifc.get_entity(bpy.context.active_object)
if not element: if not cls.element:
return results 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()}) results.append({"id": system.id(), "name": system.Name or "Unnamed", "ifc_class": system.is_a()})
return results return results
@@ -85,6 +87,12 @@ class ObjectSystemData:
def total_systems(cls): def total_systems(cls):
return len(tool.Ifc.get().by_type("IfcSystem")) 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: class PortData:
data = {} data = {}
@@ -151,15 +159,34 @@ class PortData:
class SystemDecorationData: class SystemDecorationData:
data = {} data = {}
elements_ports_positions = {}
is_loaded = False is_loaded = False
@classmethod @classmethod
def load(cls): def load(cls):
cls.data = { cls.data = {}
"decoration_data": cls.decoration_data(),
}
cls.is_loaded = True cls.is_loaded = True
cls.elements_ports_positions = {}
@classmethod @classmethod
def decoration_data(cls): def get_element_ports_data(cls, element):
return tool.System.get_decoration_data() """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]
@@ -111,11 +111,9 @@ class SystemDecorator:
roof_angle_edges = [] roof_angle_edges = []
preview_edges = [] preview_edges = []
# NOTE: using .data here doesn't allow having live viewport orientation for the calculations # NOTE: using live update because Data wouldn't allow
# but not using .data seems to be performance heavy # live time update of objects positions
if not SystemDecorationData.is_loaded: decoration_data = tool.System.get_decoration_data()
SystemDecorationData.load()
decoration_data = SystemDecorationData.data["decoration_data"]
all_vertices = decoration_data["all_vertices"] all_vertices = decoration_data["all_vertices"]
preview_edges = decoration_data["preview_edges"] preview_edges = decoration_data["preview_edges"]
+7 -2
View File
@@ -31,6 +31,7 @@ from blenderbim.bim.module.geometry.helper import Helper
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData
import collections import collections
import json import json
import numpy as np
class Model(blenderbim.core.tool.Model): 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) tool.Ifc.run("geometry.edit_object_placement", product=element, matrix=matrix, is_si=True)
@classmethod @classmethod
def get_element_matrix(cls, element): def get_element_matrix(cls, element, keep_local=False):
placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) 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) return Matrix(placement)
@classmethod @classmethod
+18 -15
View File
@@ -24,6 +24,8 @@ from blenderbim.bim import import_ifc
import re import re
from math import pi, cos, sin from math import pi, cos, sin
from mathutils import Matrix, Vector 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): class System(blenderbim.core.tool.System):
@@ -204,13 +206,16 @@ class System(blenderbim.core.tool.System):
def most_aligned_vector(a, vectors): def most_aligned_vector(a, vectors):
return max(vectors, key=lambda v: abs(a.dot(v))) 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 start_vert_i = 0
if bpy.context.active_object and (active_element := tool.Ifc.get_entity(bpy.context.active_object)): if not ObjectSystemData.is_loaded:
selected_elements = cls.get_connected_elements(active_element) ObjectSystemData.load()
else:
selected_elements = set() 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 # TODO: get only objects visible in viewport
objects = set(bpy.data.objects) - set(bpy.data.collections["Types"].objects) objects = set(bpy.data.objects) - set(bpy.data.collections["Types"].objects)
@@ -230,19 +235,17 @@ class System(blenderbim.core.tool.System):
continue continue
selected_element = element in selected_elements selected_element = element in selected_elements
ports = tool.System.get_ports(element)
verts_pos = [] verts_pos = []
for port in ports: port_data = SystemDecorationData.get_element_ports_data(element)
position = tool.Model.get_element_matrix(port).translation * si_conversion verts_pos.extend([obj.matrix_world @ data["position"] for data in port_data])
verts_pos.append(position)
verts = range(start_vert_i, start_vert_i + len(ports)) 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(ports) - 1)] 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 # 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) unique = set(flow_dirs)
if len(unique) == 1: if len(unique) == 1:
return 0 return 0
@@ -256,7 +259,7 @@ class System(blenderbim.core.tool.System):
return -1 return -1
return 0 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 = verts_pos.copy()
edge_verts = edge_verts[::flow_direction] edge_verts = edge_verts[::flow_direction]
@@ -284,7 +287,7 @@ class System(blenderbim.core.tool.System):
n_direction_lines = int(n_direction_lines) + 1 n_direction_lines = int(n_direction_lines) + 1
start_offset /= 2 start_offset /= 2
start_offset = edge_dir * start_offset + base_vert 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): for i in range(n_direction_lines):
cur_offset = start_offset + edge_dir * i * direction_lines_offset cur_offset = start_offset + edge_dir * i * direction_lines_offset