mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
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:
@@ -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"]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user