shit ton of changes

This commit is contained in:
Lucas Nascimento
2024-12-04 20:31:07 -03:00
committed by Dion Moult
parent 477725529b
commit 2912580087
7 changed files with 614 additions and 155 deletions
+1
View File
@@ -99,3 +99,4 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# Brickschema
src/bonsai/bonsai/bim/schema/Brick.ttl
bonsaiDecoratorForLoads.code-workspace
dev_environment.bat
@@ -30,6 +30,38 @@ def refresh():
StructuralLoadCasesData.is_loaded = False
StructuralLoadsData.is_loaded = False
BoundaryConditionsData.is_loaded = False
LoadGroupDecorationData.is_loaded = False
class LoadGroupDecorationData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {"load groups to show": cls.load_groups_to_show()}
cls.is_loaded = True
@classmethod
def load_groups_to_show(cls):
ret = []
abrv = {"LOAD_CASE": "L.Case: ",
"LOAD_COMBINATION": "L.Comb: ",
"LOAD_GROUP": "L.Gr: ",
"USERDEFINED": "U.Def: ",
"NOTDEFINED": "N.Def: "
}
models = tool.Ifc.get().by_type("IfcStructuralAnalysisModel")
m = models[0]
groups = m.LoadedBy or []
for g in groups:
ret.append((str(g.id()),". "+abrv[g.PredefinedType]+" "+g.Name,""))
related_objects = [rel.RelatedObjects for rel in g.IsGroupedBy]
for item in related_objects:
for subgoup in [sg for sg in item if sg.is_a("IfcStructuralLoadGroup")]:
ret.append((str(subgoup.id()),". "+abrv[subgoup.PredefinedType]+subgoup.Name,""))
if len(ret) == 0:
ret.append(("","",""))
return ret
class StructuralBoundaryConditionsData:
@@ -1,5 +1,6 @@
from math import pi
from mathutils import Vector
import numpy as np
import bpy
import gpu
import blf
@@ -14,6 +15,7 @@ class LoadsDecorator:
linear_load_shader = None
text_info = []
shader_info = []
depth_array = None
@classmethod
def install(cls, context):
@@ -28,7 +30,7 @@ class LoadsDecorator:
# SpaceView3D.draw_handler_add(handler.draw_curve_loads, (context,), "WINDOW", "POST_VIEW")
#)
cls.handlers.append(SpaceView3D.draw_handler_add(handler, (), "WINDOW", "POST_VIEW"))
cls.linear_load_shader = ShaderInfo("DistributedLoad")
cls.linear_load_shader = ShaderInfo()
cls.update(context)
cls.is_installed = True
@@ -52,7 +54,7 @@ class LoadsDecorator:
original_blend = gpu.state.blend_get()
gpu.state.blend_set('ALPHA')
original_depth_mask = gpu.state.depth_mask_get()
gpu.state.depth_mask_set(True)
#gpu.state.depth_mask_set(True)
original_depth_test = gpu.state.depth_test_get()
gpu.state.depth_test_set('LESS_EQUAL')
@@ -82,35 +84,52 @@ class LoadsDecorator:
batch.draw(shader)
def draw_load_values(self, context):
framebuffer = gpu.state.active_framebuffer_get()
viewport_info = gpu.state.viewport_get()
width = viewport_info[2]
height = viewport_info[3]
depth_buffer = framebuffer.read_depth(0, 0, width, height)
depth_array = np.array(depth_buffer.to_list())
f = context.area.spaces.active.clip_end
n = context.area.spaces.active.clip_start
self.depth_array = n / (f - (f - n) * depth_array) * (f - n)
for info in self.text_info:
text_position = location_3d_to_region_2d(info["position"],info["normal"],context)
text_position = self.location_3d_to_region_2d(info["position"],info["normal"],context)
if text_position is not None:
font_id = 0 # , need to find out how best to get this.
# draw some text
blf.position(font_id, text_position[0], text_position[1], 0)
blf.position(font_id, text_position[0], text_position[1], text_position[2])
blf.size(font_id, 20.0)
blf.color(font_id, 0.9, 0.9, 0.9, 1.0)
blf.draw(font_id, info["text"])
def location_3d_to_region_2d(coord, normal, context):
"""Convert from 3D space to 2D screen space"""
rv3d = context.region_data
perspective = rv3d.view_perspective
view_matrix = rv3d.view_matrix
point_view_space = view_matrix @ coord
def location_3d_to_region_2d(self, coord, normal, context):
"""Convert from 3D space to 2D screen space"""
rv3d = context.region_data
perspective = rv3d.view_perspective
view_matrix = rv3d.view_matrix
point_view_space = view_matrix @ coord
if perspective == 'ORTHO' or -10 < point_view_space.z < 0:
prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0))
width_half = context.region.width / 2.0
height_half = context.region.height / 2.0
co = Vector((width_half + width_half * (prj.x / prj.w),
height_half + height_half * (prj.y / prj.w),
))
view_vector = view3d_utils.region_2d_to_vector_3d(context.region, rv3d, co)
angle = abs(0.5*pi-normal.angle(view_vector))
#if small view angle betwen the load and viewport
if angle<0.05:
return None
return co
return None
if perspective == 'ORTHO' or -10 < point_view_space.z < 0:
prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0))
width_half = context.region.width / 2.0
height_half = context.region.height / 2.0
co = Vector((width_half + width_half * (prj.x / prj.w),
height_half + height_half * (prj.y / prj.w),
point_view_space.z))
if co[0]> context.region.width or co[1] > context.region.height or co[0] < 0 or co[1] < 0:
return None
depth = self.depth_array[int(co[1])][int(co[0])]
if -0.98*point_view_space.z > depth:
return None
view_vector = view3d_utils.region_2d_to_vector_3d(context.region, rv3d, co)
angle = abs(0.5*pi-normal.angle(view_vector))
#if small view angle betwen the load and viewport
if angle<0.05:
return None
return co
return None
@@ -51,8 +51,9 @@ class ShowLoads(bpy.types.Operator):
def invoke(self, context, event):
collection = bpy.data.collections["IfcStructuralItem"]
collection.hide_viewport = False
context.window.cursor_modal_set("WAIT")
LoadsDecorator.install(context)
LoadsDecorator.update(context)
context.window.cursor_modal_restore()
context.window_manager.modal_handler_add(self)
for area in context.screen.areas:
if area.type == 'VIEW_3D':
@@ -21,7 +21,7 @@ import bpy
import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
from bonsai.bim.prop import StrProperty, Attribute
from bonsai.bim.module.structural.data import StructuralLoadCasesData, StructuralLoadsData, BoundaryConditionsData
from bonsai.bim.module.structural.data import StructuralLoadCasesData, StructuralLoadsData, BoundaryConditionsData, LoadGroupDecorationData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -34,6 +34,10 @@ from bpy.props import (
CollectionProperty,
)
def get_load_groups_to_show(self, context):
if not LoadGroupDecorationData.is_loaded:
LoadGroupDecorationData.load()
return LoadGroupDecorationData.data["load groups to show"]
def get_applicable_structural_load_types(self, context):
if not StructuralLoadCasesData.is_loaded:
@@ -152,6 +156,17 @@ class BIMStructuralProperties(PropertyGroup):
boundary_condition_attributes: CollectionProperty(name="Boundary Condition Attributes", type=Attribute)
filtered_boundary_conditions: BoolProperty(name="Filtered Boundary Conditions", default=False)
show_loads: BoolProperty(name="Show Loads", default=False)
update_load_repr: BoolProperty(name="Update Load Representation", default=False)
enable_repr_auto_update: BoolProperty(name="Auto Update", default=False)
reference_frame: EnumProperty(items=[("GLOBAL_COORDS","Global","Show loads in global reference frame"),
("LOCAL_COORDS","Local","Show loads in local reference frame")], name= "Reference Frame")
activity_type: EnumProperty(items=[("Action","Actions","Show actions loads"),
("External Reaction","External Reactions","Show reactions on boundary conditions"),
("Internal Reactions","Internal Reactions","Show internal reactions on members")],
name="Activity Type")
load_group_to_show: EnumProperty(items=get_load_groups_to_show, name="Load Groups")
class BIMObjectStructuralProperties(PropertyGroup):
boundary_condition_attributes: CollectionProperty(name="Boundary Condition Attributes", type=Attribute)
+512 -127
View File
@@ -1,5 +1,6 @@
import bpy
import gpu
import bmesh
import numpy as np
from math import sin
from mathutils import Vector, Matrix
@@ -11,152 +12,319 @@ from bonsai.bim.ifc import IfcStore
class ShaderInfo:
def __init__(self,shader_type: str):
def __init__(self):
self.is_empty = True
self.shader = None
self.shader_type = shader_type
self.args = {}
self.indices = []
#self.shader = None
#self.shader_type = shader_type
#self.args = {}
#self.indices = []
self.curve_members = []
self.point_members = []
self.surface_members = {}
self.text_info = []
self.info = []
self.force_unit = ""
self.linear_force_unit = ""
self.planar_force_unit = ""
def update(self):
self.info = []
self.get_force_units()
self.get_strucutural_elements_and_activities()
self.get_linear_loads()
self.get_point_loads()
self.get_planar_loads_2()
if len(self.info):
self.is_empty = False
def get_force_units(self):
def get_unit_symbol(unit: ifcopenshell.entity_instance) -> str:
prefix_symbols = {
"EXA": "E",
"PETA": "P",
"TERA": "T",
"GIGA": "G",
"MEGA": "M",
"KILO": "k",
"HECTO": "h",
"DECA": "da",
"DECI": "d",
"CENTI": "c",
"MILLI": "m",
"MICRO": "μ",
"NANO": "n",
"PICO": "p",
"FEMTO": "f",
"ATTO": "a",
}
unit_symbols = {
# si units
"CUBIC_METRE": "m3",
"GRAM": "g",
"SECOND": "s",
"SQUARE_METRE": "m2",
"METRE": "m",
"NEWTON": "N",
"PASCAL": "Pa",
# conversion based units
"pound-force": "lbf",
'pound-force per square inch': "psi",
"thou": "th",
"inch": "in",
"foot": "ft",
"yard": "yd",
"mile": "mi",
"square thou": "th2",
"square inch": "in2",
"square foot": "ft2",
"square yard": "yd2",
"acre": "ac",
"square mile": "mi2",
"cubic thou": "th3",
"cubic inch": "in3",
"cubic foot": "ft3",
"cubic yard": "yd3",
"cubic mile": "mi3",
"litre": "L",
"fluid ounce UK": "fl oz",
"fluid ounce US": "fl oz",
"pint UK": "pt",
"pint US": "pt",
"gallon UK": "gal",
"gallon US": "gal",
"degree": "°",
"ounce": "oz",
"pound": "lb",
"ton UK": "ton",
"ton US": "ton",
"lbf": "lbf",
"kip": "kip",
"psi": "psi",
"ksi": "ksi",
"minute": "min",
"hour": "hr",
"day": "day",
"btu": "btu",
"fahrenheit": "°F",
}
symbol = ""
if unit.is_a("IfcSIUnit"):
symbol += prefix_symbols.get(unit.Prefix, "")
symbol += unit_symbols.get(unit.Name.replace("METER", "METRE"), "?")
return symbol
force_units = [u for u in tool.Ifc.get().by_type("IfcNamedUnit")
if u.UnitType == "FORCEUNIT"]
linear_force_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit")
if u.UnitType == "LINEARFORCEUNIT"]
planar_force_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit")
if u.UnitType == "PLANARFORCEUNIT"]
conversion_force_unit = [u for u in force_units if u.is_a("IfcConversionBasedUnit")]
if len(conversion_force_unit) == 0:
conversion_force_unit.append(force_units[0])
self.force_unit = get_unit_symbol(conversion_force_unit[0])
first = ""
second = ""
for e in linear_force_units[0].Elements:
if e.Unit.UnitType == "FORCEUNIT":
first = get_unit_symbol(e.Unit)
if e.Unit.UnitType == "LENGTHUNIT":
second = get_unit_symbol(e.Unit)
self.linear_force_unit = first + "/" + second
first = ""
second = ""
for e in planar_force_units[0].Elements:
if e.Unit.UnitType == "FORCEUNIT":
first = get_unit_symbol(e.Unit)
if e.Unit.UnitType == "LENGTHUNIT":
second = get_unit_symbol(e.Unit)+"2"
if e.Unit.UnitType == "AREAUNIT":
second = get_unit_symbol(e.Unit)
self.planar_force_unit = first + "/" + second
def get_strucutural_elements_and_activities(self):
def populate_members_dict(dict_name,element, activity, factor):
dic = getattr(self,dict_name,None)
if dic is None:
return
member = dic.get(element.GlobalId)
if member is None:
dic.update({
element.GlobalId: {
"member": element,
"activities": [(activity,factor)]}
})
else:
member["activities"].append((activity,factor))
def recursive_subgroups(groups, rec_limit, factor = 1):
if len(groups) == 0 or rec_limit == 0:
return None
for group in groups:
subgorups = []
activities = []
relationship = [rel for rel in group.IsGroupedBy]
group_coef = group.Coefficient if group.Coefficient is not None else 1.0
rel_factor = 1.0
for rel in relationship:
if rel.is_a("IfcRelAssignsToGroupByFactor"):
rel_factor = rel.Factor if rel.Factor is not None else 1.0
objects = rel.RelatedObjects
subgorups = [sg for sg in objects if sg.is_a("IfcStructuralLoadGroup")]
activities = [a for a in objects if a.is_a("IfcStructuralActivity")]
factor = factor*group_coef*rel_factor
for activity in activities:
if len(activity.AssignedToStructuralItem):
element = activity.AssignedToStructuralItem[0].RelatingElement
if element is not None:
if element.is_a("IfcStructuralCurveMember"):
self.curve_members.append(element)
elif element.is_a("IfcStructuralPoinConnection"):
self.point_members.append(element)
elif element.is_a("IfcStructuralSurfaceMember"):
populate_members_dict("surface_members",element,activity,factor)
recursive_subgroups(subgorups,rec_limit-1,factor=factor)
props = bpy.context.scene.BIMStructuralProperties
group_definition_id = int(props.load_group_to_show)
file = IfcStore.get_file()
groups = [file.by_id(group_definition_id)]
recursive_subgroups(groups,10)
def get_shader(self, pattern):
""" param: pattern: type of pattern in ["force", "force match", "moment"]
return: shader"""
if self.shader_type == "DistributedLoad":
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "forces")
vert_out.smooth('VEC3', "co")
#if self.shader_type == "DistributedLoad":
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "forces")
vert_out.smooth('VEC3', "co")
shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color")
shader_info.push_constant('FLOAT', "spacing")
shader_info.push_constant('FLOAT', "maxload")
shader_info.vertex_in(0, 'VEC3', "position")
shader_info.vertex_in(1, 'VEC3', "sin_quad_lin_forces")
shader_info.vertex_in(2, 'VEC3', "coord")
shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color")
shader_info.push_constant('FLOAT', "spacing")
shader_info.push_constant('FLOAT', "maxload")
shader_info.vertex_in(0, 'VEC3', "position")
shader_info.vertex_in(1, 'VEC3', "sin_quad_lin_forces")
shader_info.vertex_in(2, 'VEC3', "coord")
shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor")
shader_info.vertex_source(
"void main()"
"{"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
" co = coord;"
" forces = sin_quad_lin_forces;"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
"}"
)
if pattern == "force":
shader_info.fragment_source(
shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor")
shader_info.vertex_source(
"void main()"
"{"
"float x = co.x;"
"float y = co.y;"
"float abs_y = abs(y);"
"float a = abs(mod(x,spacing)-0.5*spacing)*5.0;"
"float b = step(a,abs_y)*(step(abs_y,1.2*spacing));"
"float c = step(0.8*spacing,mod(x+0.4*spacing,spacing))*(step(1.2*spacing,abs_y));"
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);"
"float top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(0.1+top+b+c,0.0,0.5)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
" co = coord;"
" forces = sin_quad_lin_forces;"
"}"
)
if pattern == "force match":
shader_info.fragment_source(
"void main()"
"{"
"float y = co.y;"
"float x = step(0.,y)*(co.z-co.x)+step(y,0.)*(co.x);"
"float abs_y = abs(y);"
)
if pattern == "force":
shader_info.fragment_source(
"void main()"
"{"
"float x = co.x;"
"float y = co.y;"
"float abs_y = abs(y);"
"float a = abs(mod(x,spacing)-0.5*spacing)*5.0;"
"float b = step(a,abs_y)*(step(abs_y,1.2*spacing));"
"float c = step(0.8*spacing,mod(x+0.4*spacing,spacing))*(step(1.2*spacing,abs_y));"
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);"
"float a = abs(mod(abs_y,spacing)-0.5*spacing)*5.0;"
"float a2 = mod(x,3.0*spacing);"
"float b = step(a,a2)*step(a2,1.2*spacing);"
"float c = step(0.8*spacing,mod(abs_y+0.4*spacing,spacing))"
"*(step(1.2*spacing,a2))*step(a2,2.5*spacing);"
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);"
"float top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(0.1+top+b+c,0.0,0.5)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)
"float top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(top+b+c,0.0,0.9)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)
if pattern == "moment":
shader_info.fragment_source(
"void main()"
"{"
"float x = step(co.y,0.)*(co.x)+step(0.,co.y)*(co.z-co.x);"
"float y = step(co.y,-0.00001)*(co.y)+step(0.,co.y)*(0.-co.y);"
"x = mod((0.5/spacing)*x,1.4)-0.7;"
"y = mod((0.5/spacing)*y,1.4)-0.7;"
"float abs_y = abs(y);"
"vec2 st = vec2(1.9*x,y);"
"vec2 orig = vec2(0.,0.);"
if pattern == "force match":
shader_info.fragment_source(
"void main()"
"{"
"float y = co.y;"
"float x = step(0.,y)*(co.z-co.x)+step(y,0.)*(co.x);"
"float abs_y = abs(y);"
"float a = abs(mod(abs_y,spacing)-0.5*spacing)*5.0;"
"float a2 = mod(x,3.0*spacing);"
"float b = step(a,a2)*step(a2,1.2*spacing);"
"float c = step(0.8*spacing,mod(abs_y+0.4*spacing,spacing))"
"*(step(1.2*spacing,a2))*step(a2,2.5*spacing);"
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);"
"float circ = step(distance(st,orig),0.33)*step(0.27,distance(st,orig));"
"float tri_mask = step(st.y,st.x)+step(-st.x,st.y);"
"float circ_arrow = step(st.x,4.0*st.y-0.75)*step(0.25*st.y-0.34,st.x)*(1.-tri_mask);"
"float top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(top+b+c,0.0,0.9)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)
"float circmask = step(distance(st,orig),0.1)+step(0.5,distance(st,orig))+step(st.x,0.);"
"float body = step(-0.03,st.y)*step(st.y,0.03)*step(-0.3,x)*step(x,0.576);"
"float body_arrow = step(-0.5+3.*st.y,x)*step(-0.5-3.*st.y,x)*step(x,-0.3);"
"float d = clamp(circmask*(body+body_arrow)+circ_arrow+circ*tri_mask,0.,1.);"
if pattern == "moment":
shader_info.fragment_source(
"void main()"
"{"
"float x = step(co.y,0.)*(co.x)+step(0.,co.y)*(co.z-co.x);"
"float y = step(co.y,-0.00001)*(co.y)+step(0.,co.y)*(0.-co.y);"
"x = mod((0.5/spacing)*x,1.4)-0.7;"
"y = mod((0.5/spacing)*y,1.4)-0.7;"
"float abs_y = abs(y);"
"vec2 st = vec2(1.9*x,y);"
"vec2 orig = vec2(0.,0.);"
"float circ = step(distance(st,orig),0.33)*step(0.27,distance(st,orig));"
"float tri_mask = step(st.y,st.x)+step(-st.x,st.y);"
"float circ_arrow = step(st.x,4.0*st.y-0.75)*step(0.25*st.y-0.34,st.x)*(1.-tri_mask);"
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,co.y)*step(co.y,f)+step(co.y,0.)*step(f,co.y);"
"float top = step(abs(co.y-f),0.2*1.2*spacing);"
"float circmask = step(distance(st,orig),0.1)+step(0.5,distance(st,orig))+step(st.x,0.);"
"float body = step(-0.03,st.y)*step(st.y,0.03)*step(-0.3,x)*step(x,0.576);"
"float body_arrow = step(-0.5+3.*st.y,x)*step(-0.5-3.*st.y,x)*step(x,-0.3);"
"float d = clamp(circmask*(body+body_arrow)+circ_arrow+circ*tri_mask,0.,1.);"
"d = clamp(0.1+top+d,0.0,0.5)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)
"float sinvalue = forces.x;"
"float quadraticvalue = forces.y;"
"float linearvalue = forces.z;"
"x = co.x/co.z;"
"float f = (sin(x*3.1416)*sinvalue"
"+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;"
"float mask = step(0.,co.y)*step(co.y,f)+step(co.y,0.)*step(f,co.y);"
shader = gpu.shader.create_from_info(shader_info)
del vert_out
del shader_info
return shader
"float top = step(abs(co.y-f),0.2*1.2*spacing);"
"d = clamp(top+d,0.0,0.9)*mask;"
"if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)
shader = gpu.shader.create_from_info(shader_info)
del vert_out
del shader_info
return shader
def get_point_shader(self, pattern):
""" param: pattern: type of pattern in ["arrow", "circ arrow"]
@@ -212,6 +380,220 @@ class ShaderInfo:
del vert_out
del shader_info
return shader
def get_planar_shader(self):
""" param: pattern: type of pattern in ["force", "force match", "moment"]
return: shader"""
#if self.shader_type == "DistributedLoad":
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "co")
shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color")
shader_info.push_constant('FLOAT', "spacing")
shader_info.vertex_in(0, 'VEC3', "position")
shader_info.vertex_in(1, 'VEC3', "coord")
shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor")
shader_info.vertex_source(
"void main()"
"{"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
" co = coord;"
"}"
)
shader_info.fragment_source(
"void main()"
"{"
"float x = co.x;"
"float y = co.y;"
"float abs_y = abs(y);"
"float a = abs(mod(x,spacing)-0.5*spacing)*5.0;"
"float b = step(a,abs_y)*(step(abs_y,1.2*spacing));"
"float c = step(0.8*spacing,mod(x+0.4*spacing,spacing))*(step(1.2*spacing,abs_y));"
"float mask = step(0.,y)*step(y,0.98)+step(y,0.)*step(0.98,y);"
"float top = step(abs(y-0.98),0.2*1.2*spacing);"
"float d = clamp(0.2*y+(top+b+c)*mask,0.0,0.4);"
"FragColor = vec4(color.xyz,d*color.w);"
"}"
)#"float d = clamp(0.1+top+b+c,0.0,0.4);"
shader = gpu.shader.create_from_info(shader_info)
del vert_out
del shader_info
return shader
def get_planar_loads(self):
list_of_surfaces = self.surface_members
shader = self.get_planar_shader()
for value in list_of_surfaces.values():
surf = value["element"]
activity_list = [getattr(a, 'RelatedStructuralActivity', None) for a in getattr(surf, 'AssignedStructuralActivity', None)
if getattr(a, 'RelatedStructuralActivity', None).is_a() in ['IfcStructuralPlanarAction','IfcStructuralSurfaceAction']]
if len(activity_list) == 0:
continue
blender_object: bpy.types.Object = IfcStore.get_element(getattr(surf, 'GlobalId', None))
mat = blender_object.matrix_world
mesh: bpy.types.Mesh = blender_object.data
z = Vector((0,0,1))
positions = []
indices = []
coord = []
for p in mesh.polygons:
add_index = len(positions)
for i, v in enumerate(p.vertices):
p1 = mesh.vertices[v].co
positions.append(p1)
p2 = p1 + z
positions.append(p2)
if i == 0:
coord.append((0,0,1))
coord.append((0,1,1))
else:
vec = positions[-1] - positions [-3]
l = vec.length
coord.append((l,0,1))
coord.append((l,1,1))
add = add_index+2*i
indices.append((0+add,2+add,1+add))
indices.append((1+add,2+add,3+add))
length = len(p.vertices)-1
indices.append((length+add_index-1,length+add_index,0+add_index))
indices.append((length+add_index,0+add_index,1+add_index))
indices.append((1+add_index,3+add_index,5+add_index))
if len(p.vertices) > 3:
indices.append((1+add_index,5+add_index,7+add_index))
self.info.append(
{
"shader": shader,
"args": {"position": positions, "coord": coord},
"indices": indices,
"uniforms": [["color", (0.2,0,1,1)],["spacing", 0.2]]
}
)
def get_planar_loads_2(self):
list_of_surfaces = self.surface_members
shader = self.get_planar_shader()
maximum = 0
for value in list_of_surfaces.values():
surf = value["member"]
activity_list = [getattr(a, 'RelatedStructuralActivity', None) for a in getattr(surf, 'AssignedStructuralActivity', None)
if getattr(a, 'RelatedStructuralActivity', None).is_a() in ['IfcStructuralPlanarAction','IfcStructuralSurfaceAction']]
activity_list = value["activities"]
if len(activity_list) == 0:
continue
values = self.get_planar_loads_values(activity_list)
if maximum == 0:
maximum = max([abs(float(i)) for i in values])
if maximum == 0:
continue
blender_object: bpy.types.Object = IfcStore.get_element(getattr(surf, 'GlobalId', None))
mat = blender_object.matrix_world
mesh: bpy.types.Mesh = blender_object.data
rotation = self.get_surface_member_rotation(surf)
x = rotation @ (np.array((1,0,0))*0.2)
y = rotation @ (np.array((0,1,0))*0.2)
z = rotation @ (np.array((0,0,1))*0.2)
positions = []
indices = []
coord = []
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.triangulate(bm, faces = bm.faces)
bm.edges.ensure_lookup_table()
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
add_index = len(positions)
for v in bm.verts:
p1 = np.array(mat @ v.co)
positions.append(p1)
p2 = p1 - (rotation@values)*0.2/maximum
positions.append(p2)
coord.append((float(p1[0]+p1[1]),0,1))
coord.append((float(p1[0]+p1[1]),1,1))
for e in bm.edges:
if len(e.link_faces) > 1:
continue
indices.append((2*e.verts[0].index,
2*e.verts[0].index+1,
2*e.verts[1].index))
indices.append((2*e.verts[0].index+1,
2*e.verts[1].index,
2*e.verts[1].index+1))
for p in bm.faces:
indices.append((2*p.verts[0].index+1,
2*p.verts[1].index+1,
2*p.verts[2].index+1))
bmesh.ops.dissolve_limit(bm, angle_limit = 0.01, verts = bm.verts, edges = bm.edges)
bm.faces.ensure_lookup_table()
center = bm.faces[0].calc_center_bounds()
self.text_info.append(
{"position": mat @ center - Vector((rotation@values)*0.2/maximum),
"normal": Vector(mesh.polygons[0].normal),
"text": f'{values[2]:.5f} {self.planar_force_unit}'}
)
self.info.append(
{
"shader": shader,
"args": {"position": positions, "coord": coord},
"indices": indices,
"uniforms": [["color", (0.2,0,1,1)],["spacing", 0.2]]
}
)
def get_planar_loads_values(self, activity_list):
values = np.zeros((3))
for item in activity_list:
activity = item[0]
factor = item[1]
load = activity.AppliedLoad
temp = np.zeros((3))
if load is not None and load.is_a("IfcStructuralLoadPlanarForce"):
temp[0] = load.PlanarForceX if load.PlanarForceX is not None else 0
temp[1] = load.PlanarForceY if load.PlanarForceY is not None else 0
temp[2] = load.PlanarForceZ if load.PlanarForceZ is not None else 0
temp = temp*factor
values = values + temp
return values
def get_surface_member_rotation(self,surface_member):
representation = ifcopenshell.util.representation.get_representation(surface_member, "Model")
repr_item = representation.Items[0]
placement = ifcopenshell.util.placement.get_axis2placement(repr_item.FaceSurface.Position)
rotation = placement[0:3,0:3]
return rotation
def get_point_connection_rotation(self, point_connection):
if point_connection.ConditionCoordinateSystem is not None:
placement = ifcopenshell.util.placement.get_axis2placement(point_connection.ConditionCoordinateSystem)
else:
placement = np.eye(4)
rotation = placement[0:3,0:3]
return rotation
def get_curve_member_rotation(self, curve_member):
z = curve_member.Axis.DirectionRatios
edge = curve_member.Representation.Representations[0].Items[0]
origin = edge.EdgeStart.VertexGeometry.Coordinates
end = edge.EdgeEnd.VertexGeometry.Coordinates
x = [c2 - c1 for c1, c2 in zip(origin, end)]
placement = ifcopenshell.util.placement.a2p(origin,z,x)
rotation = placement[0:3,0:3]
return rotation
def get_point_loads(self):
list_of_point_connections = tool.Ifc.get().by_type("IfcStructuralPointConnection")
@@ -223,9 +605,9 @@ class ShaderInfo:
blender_object = IfcStore.get_element(getattr(conn, 'GlobalId', None))
if blender_object.type == 'MESH':
conn_location = blender_object.matrix_world @ blender_object.data.vertices[0].co
#get local coordinates of the connection
loads = self.get_point_loads_list(activity_list)
self.get_point_shader_args(loads, conn_location)
#get local coordinates of the connection
loads = self.get_point_loads_list(activity_list)
self.get_point_shader_args(loads, conn_location)
def get_point_shader_args(self,loads, location):
indices = []
@@ -288,7 +670,7 @@ class ShaderInfo:
"shader": shader,
"args": {"position": position,"coord": coords_for_shader},
"indices": indices,
"uniforms": [["color", color]]#,["spacing", 0.2]]
"uniforms": [["color", color]]
}
)
@@ -316,6 +698,7 @@ class ShaderInfo:
info = []
list_of_curve_members = tool.Ifc.get().by_type("IfcStructuralCurveMember")
list_of_curve_members = self.curve_members
for member in list_of_curve_members:
activity_list = [getattr(a, 'RelatedStructuralActivity', None) for a in getattr(member, 'AssignedStructuralActivity', None)
if getattr(a, 'RelatedStructuralActivity', None).is_a() in ['IfcStructuralCurveAction','IfcStructuralLinearAction']]
@@ -403,7 +786,8 @@ class ShaderInfo:
if func:
text_info.append(
{"position": -1*direction*func/maxforce + start_co + x_axis*current.x,
"normal": direction.cross(x_axis).normalized(), "text": f'{func:.2f} '}
"normal": direction.cross(x_axis).normalized(),
"text": f'{func:.2f} {self.linear_force_unit}'}
)
maxforce = max(maxforce,abs(func))
position.append(positive)
@@ -430,7 +814,8 @@ class ShaderInfo:
if func:
text_info.append(
{"position": -1*direction*func/maxforce + start_co + x_axis*nextitem.x,
"normal": direction.cross(x_axis).normalized(), "text": f'{func:.2f} '}
"normal": direction.cross(x_axis).normalized(),
"text": f'{func:.2f} {self.linear_force_unit}'}
)
maxforce = max(maxforce,abs(func))
position.append(positive)
@@ -465,8 +465,14 @@ class BIM_PT_show_structural_activities(Panel):
row.operator(
"bim.show_loads",
text="Show loads" ,
icon="FILTER",
icon="HIDE_OFF",
)
row = self.layout.row(align=True)
row.prop(self.props,"reference_frame")
row = self.layout.row(align=True)
row.prop(self.props,"activity_type")
row = self.layout.row(align=True)
row.prop(self.props,"load_group_to_show")
class BIM_PT_structural_loads(Panel):