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 cc1ee19115
commit 1193e7025a
5 changed files with 71 additions and 33 deletions
@@ -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 = []
@@ -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]
@@ -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"]
+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
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
+18 -15
View File
@@ -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