black formatting and changes in shader typing

This commit is contained in:
Lucas Nascimento
2024-12-18 14:46:46 -03:00
committed by Dion Moult
parent 80996dee1f
commit dff9f56ce3
7 changed files with 644 additions and 640 deletions
@@ -32,6 +32,7 @@ def refresh():
BoundaryConditionsData.is_loaded = False BoundaryConditionsData.is_loaded = False
LoadGroupDecorationData.is_loaded = False LoadGroupDecorationData.is_loaded = False
class LoadGroupDecorationData: class LoadGroupDecorationData:
data = {} data = {}
is_loaded = False is_loaded = False
@@ -44,11 +45,12 @@ class LoadGroupDecorationData:
@classmethod @classmethod
def load_groups_to_show(cls): def load_groups_to_show(cls):
ret = [] ret = []
abrv = {"LOAD_CASE": "L.Case: ", abrv = {
"LOAD_CASE": "L.Case: ",
"LOAD_COMBINATION": "L.Comb: ", "LOAD_COMBINATION": "L.Comb: ",
"LOAD_GROUP": "L.Gr: ", "LOAD_GROUP": "L.Gr: ",
"USERDEFINED": "U.Def: ", "USERDEFINED": "U.Def: ",
"NOTDEFINED": "N.Def: " "NOTDEFINED": "N.Def: ",
} }
models = tool.Ifc.get().by_type("IfcStructuralAnalysisModel") models = tool.Ifc.get().by_type("IfcStructuralAnalysisModel")
m = models[0] m = models[0]
@@ -26,8 +26,10 @@ from gpu_extras.batch import batch_for_shader
from typing import Iterable, Union from typing import Iterable, Union
from bonsai.bim.module.structural.load_decoration_data import ShaderInfo from bonsai.bim.module.structural.load_decoration_data import ShaderInfo
class LoadsDecorator: class LoadsDecorator:
"""Decorator to show strucutural loads in 3D""" """Decorator to show strucutural loads in 3D"""
is_installed = False is_installed = False
handlers = [] handlers = []
decoration_data = None decoration_data = None
@@ -68,8 +70,8 @@ class LoadsDecorator:
# set open gl configurations # set open gl configurations
original_blend = gpu.state.blend_get() original_blend = gpu.state.blend_get()
original_depth_test = gpu.state.depth_test_get() original_depth_test = gpu.state.depth_test_get()
gpu.state.blend_set('ALPHA') gpu.state.blend_set("ALPHA")
gpu.state.depth_test_set('LESS_EQUAL') gpu.state.depth_test_set("LESS_EQUAL")
self.draw_batch() self.draw_batch()
@@ -84,7 +86,7 @@ class LoadsDecorator:
shader = info["shader"] shader = info["shader"]
args = info["args"] args = info["args"]
indices = info["indices"] indices = info["indices"]
batch = batch_for_shader(shader, 'TRIS', args, indices=indices) batch = batch_for_shader(shader, "TRIS", args, indices=indices)
matrix = bpy.context.region_data.perspective_matrix matrix = bpy.context.region_data.perspective_matrix
shader.bind() shader.bind()
shader.uniform_float("viewProjectionMatrix", matrix) shader.uniform_float("viewProjectionMatrix", matrix)
@@ -129,15 +131,23 @@ class LoadsDecorator:
view_matrix = rv3d.view_matrix view_matrix = rv3d.view_matrix
point_view_space = view_matrix @ coord point_view_space = view_matrix @ coord
if perspective == "ORTHO" or -10 < point_view_space.z < 0:
if perspective == 'ORTHO' or -10 < point_view_space.z < 0:
prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0)) prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0))
width_half = context.region.width / 2.0 width_half = context.region.width / 2.0
height_half = context.region.height / 2.0 height_half = context.region.height / 2.0
coord_2d = Vector((width_half + width_half * (prj.x / prj.w), coord_2d = Vector(
(
width_half + width_half * (prj.x / prj.w),
height_half + height_half * (prj.y / prj.w), height_half + height_half * (prj.y / prj.w),
point_view_space.z)) point_view_space.z,
if coord_2d[0] < 0 or coord_2d[0]> context.region.width or coord_2d[1] > context.region.height or coord_2d[1] < 0: )
)
if (
coord_2d[0] < 0
or coord_2d[0] > context.region.width
or coord_2d[1] > context.region.height
or coord_2d[1] < 0
):
return None return None
depth = self.depth_array[int(coord_2d[1])][int(coord_2d[0])] depth = self.depth_array[int(coord_2d[1])][int(coord_2d[0])]
if -0.98 * point_view_space.z > depth: if -0.98 * point_view_space.z > depth:
@@ -30,38 +30,35 @@ from bonsai.bim.ifc import IfcStore
from bonsai.bim.module.structural.shader import DecorationShader from bonsai.bim.module.structural.shader import DecorationShader
from typing import Literal, TypedDict, Iterable from typing import Literal, TypedDict, Iterable
MemberInfo = TypedDict("MemberInfo", MemberInfo = TypedDict(
{"member": ifcopenshell.entity_instance, "MemberInfo",
"activities": list[tuple[ifcopenshell.entity_instance,float]]}) {"member": ifcopenshell.entity_instance, "activities": list[tuple[ifcopenshell.entity_instance, float]]},
)
LoadConfigItem = TypedDict("LoadConfigItem", LoadConfigItem = TypedDict(
{"pos": float, "LoadConfigItem", {"pos": float, "descr": Literal["start", "end", "middle"], "load values": np.ndarray}
"descr": Literal["start","end", "middle"], )
"load values":np.ndarray})
DiscreteConfigItem = TypedDict("DiscreteConfigItem", DiscreteConfigItem = TypedDict("DiscreteConfigItem", {"pos": float, "values": list[float]})
{"pos": float,
"values": list[float]})
ParsedLoad = TypedDict("ParsedLoad", ParsedLoad = TypedDict(
{"constant force": list[float], "ParsedLoad",
{
"constant force": list[float],
"quadratic force": list[float], "quadratic force": list[float],
"sinus force": list[float], "sinus force": list[float],
"linear load configuration": list[list[LoadConfigItem]], "linear load configuration": list[list[LoadConfigItem]],
"point load configuration": list[list[DiscreteConfigItem]] "point load configuration": list[list[DiscreteConfigItem]],
}) },
LoadByDirection = TypedDict("LoadByDirection", )
{"constant": float, LoadByDirection = TypedDict(
"quadratic": float, "LoadByDirection", {"constant": float, "quadratic": float, "sinus": float, "polyline": list[list[float]]}
"sinus": float, )
"polyline":list[list[float]]})
ProcessedLoad = TypedDict("ProcessedLoad",
{"linear loads":LoadByDirection,
"max linear load": float,
"discrete loads": list[list[DiscreteConfigItem]]})
ProcessedLoad = TypedDict(
"ProcessedLoad",
{"linear loads": LoadByDirection, "max linear load": float, "discrete loads": list[list[DiscreteConfigItem]]},
)
class ShaderInfo: class ShaderInfo:
@@ -125,7 +122,7 @@ class ShaderInfo:
"PASCAL": "Pa", "PASCAL": "Pa",
# conversion based units # conversion based units
"pound-force": "lbf", "pound-force": "lbf",
'pound-force per square inch': "psi", "pound-force per square inch": "psi",
"thou": "th", "thou": "th",
"inch": "in", "inch": "in",
"foot": "ft", "foot": "ft",
@@ -170,16 +167,11 @@ class ShaderInfo:
symbol += unit_symbols.get(unit.Name.replace("METER", "METRE"), "?") symbol += unit_symbols.get(unit.Name.replace("METER", "METRE"), "?")
return symbol return symbol
length_units = [u for u in tool.Ifc.get().by_type("IfcNamedUnit") length_units = [u for u in tool.Ifc.get().by_type("IfcNamedUnit") if u.UnitType == "LENGTHUNIT"]
if u.UnitType == "LENGTHUNIT"] force_units = [u for u in tool.Ifc.get().by_type("IfcNamedUnit") if u.UnitType == "FORCEUNIT"]
force_units = [u for u in tool.Ifc.get().by_type("IfcNamedUnit") linear_force_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit") if u.UnitType == "LINEARFORCEUNIT"]
if u.UnitType == "FORCEUNIT"] linear_moment_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit") if u.UnitType == "LINEARMOMENTUNIT"]
linear_force_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit") planar_force_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit") if u.UnitType == "PLANARFORCEUNIT"]
if u.UnitType == "LINEARFORCEUNIT"]
linear_moment_units = [u for u in tool.Ifc.get().by_type("IfcDerivedUnit")
if u.UnitType == "LINEARMOMENTUNIT"]
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")] conversion_force_unit = [u for u in force_units if u.is_a("IfcConversionBasedUnit")]
if len(conversion_force_unit) == 0: if len(conversion_force_unit) == 0:
@@ -224,10 +216,12 @@ class ShaderInfo:
def get_strucutural_elements_and_activities(self) -> None: def get_strucutural_elements_and_activities(self) -> None:
"""fills self.point_members, self.curve_members and self.surface_members dictionaries""" """fills self.point_members, self.curve_members and self.surface_members dictionaries"""
def populate_members_dict(dict_name: Literal["point_members", "curve_members", "surface_members"], def populate_members_dict(
dict_name: Literal["point_members", "curve_members", "surface_members"],
element: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance,
activity: ifcopenshell.entity_instance, activity: ifcopenshell.entity_instance,
factor: float) -> None: factor: float,
) -> None:
""" """
fills self.point_members, self.curve_members and self.surface_members dictionaries fills self.point_members, self.curve_members and self.surface_members dictionaries
thoses dicts will contain the strucutural member global id as key and a second dict as value thoses dicts will contain the strucutural member global id as key and a second dict as value
@@ -246,18 +240,16 @@ class ShaderInfo:
return return
member = dic.get(element.GlobalId) member = dic.get(element.GlobalId)
if member is None: if member is None:
dic.update({ dic.update({element.GlobalId: {"member": element, "activities": [(activity, factor)]}})
element.GlobalId: {
"member": element,
"activities": [(activity,factor)]}
})
else: else:
member["activities"].append((activity, factor)) member["activities"].append((activity, factor))
def recursive_subgroups(groups: list[ifcopenshell.entity_instance], def recursive_subgroups(
groups: list[ifcopenshell.entity_instance],
rec_limit: int, rec_limit: int,
activity_type: Literal["Action", "External Reaction"], activity_type: Literal["Action", "External Reaction"],
factor: float = 1) -> None: factor: float = 1,
) -> None:
""" """
Recursively fills self.point_members, self.curve_members and self.surface_members dictionaries Recursively fills self.point_members, self.curve_members and self.surface_members dictionaries
with the structural members to wich the activities in the load group and its subgroups are applied with the structural members to wich the activities in the load group and its subgroups are applied
@@ -278,7 +270,7 @@ class ShaderInfo:
subgorups = [] subgorups = []
activities = [] activities = []
relationship = [rel for rel in group.IsGroupedBy] relationship = [rel for rel in group.IsGroupedBy]
coef = getattr(group, 'Coefficient', 1.0) coef = getattr(group, "Coefficient", 1.0)
group_coef = coef if coef is not None else 1.0 group_coef = coef if coef is not None else 1.0
rel_factor = 1.0 rel_factor = 1.0
@@ -301,7 +293,10 @@ class ShaderInfo:
elif element.is_a("IfcStructuralSurfaceMember"): elif element.is_a("IfcStructuralSurfaceMember"):
populate_members_dict("surface_members", element, activity, factor) populate_members_dict("surface_members", element, activity, factor)
elif activity_type == "External Reaction" and getattr(element,"AppliedCondition",None) is not None: elif (
activity_type == "External Reaction"
and getattr(element, "AppliedCondition", None) is not None
):
if element.is_a("IfcStructuralCurveMember"): if element.is_a("IfcStructuralCurveMember"):
populate_members_dict("curve_members", element, activity, factor) populate_members_dict("curve_members", element, activity, factor)
elif element.is_a("IfcStructuralPointConnection"): elif element.is_a("IfcStructuralPointConnection"):
@@ -338,7 +333,7 @@ class ShaderInfo:
orientation = np.eye(3) orientation = np.eye(3)
if reference_frame == "LOCAL_COORDS": if reference_frame == "LOCAL_COORDS":
orientation = rotation orientation = rotation
blender_object: bpy.types.Object = IfcStore.get_element(getattr(surf, 'GlobalId', None)) blender_object: bpy.types.Object = IfcStore.get_element(getattr(surf, "GlobalId", None))
mat = blender_object.matrix_world mat = blender_object.matrix_world
mesh: bpy.types.Mesh = blender_object.data mesh: bpy.types.Mesh = blender_object.data
@@ -362,23 +357,19 @@ class ShaderInfo:
for e in bm.edges: for e in bm.edges:
if len(e.link_faces) > 1: if len(e.link_faces) > 1:
continue continue
indices.append((2*e.verts[0].index, indices.append((2 * e.verts[0].index, 2 * e.verts[0].index + 1, 2 * e.verts[1].index))
2*e.verts[0].index+1, indices.append((2 * e.verts[0].index + 1, 2 * e.verts[1].index, 2 * e.verts[1].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: for p in bm.faces:
indices.append((2*p.verts[0].index+1, indices.append((2 * p.verts[0].index + 1, 2 * p.verts[1].index + 1, 2 * p.verts[2].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) bmesh.ops.dissolve_limit(bm, angle_limit=0.01, verts=bm.verts, edges=bm.edges)
bm.faces.ensure_lookup_table() bm.faces.ensure_lookup_table()
center = bm.faces[0].calc_center_bounds() center = bm.faces[0].calc_center_bounds()
self.text_info.append( self.text_info.append(
{"position": mat @ center - Vector((orientation@values)*0.2/maximum), {
"text": f'{values[2]:.5f} {self.planar_force_unit}'} "position": mat @ center - Vector((orientation @ values) * 0.2 / maximum),
"text": f"{values[2]:.5f} {self.planar_force_unit}",
}
) )
self.info.append( self.info.append(
@@ -386,12 +377,13 @@ class ShaderInfo:
"shader": shader, "shader": shader,
"args": {"position": positions, "coord": coord}, "args": {"position": positions, "coord": coord},
"indices": indices, "indices": indices,
"uniforms": [["color", (0.2,0,1,1)],["spacing", 0.2]] "uniforms": [["color", (0.2, 0, 1, 1)], ["spacing", 0.2]],
} }
) )
def get_planar_loads_values(self,
activity_list: list[tuple[ifcopenshell.entity_instance,float]], def get_planar_loads_values(
element_rotation_matrix: np.ndarray) -> np.ndarray: self, activity_list: list[tuple[ifcopenshell.entity_instance, float]], element_rotation_matrix: np.ndarray
) -> np.ndarray:
""" """
returns a numpy array with the sum of the values of structural activities returns a numpy array with the sum of the values of structural activities
applied loads in each direction, multiplied by the factors in load combinations applied loads in each direction, multiplied by the factors in load combinations
@@ -411,9 +403,7 @@ class ShaderInfo:
values += transform @ temp values += transform @ temp
return values return values
def get_surface_member_rotation(self, def get_surface_member_rotation(self, surface_member: ifcopenshell.entity_instance) -> np.ndarray:
surface_member: ifcopenshell.entity_instance
) -> np.ndarray:
"""returns the rotation matrix of a structural surface member""" """returns the rotation matrix of a structural surface member"""
representation = ifcopenshell.util.representation.get_representation(surface_member, "Model") representation = ifcopenshell.util.representation.get_representation(surface_member, "Model")
repr_item = representation.Items[0] repr_item = representation.Items[0]
@@ -421,9 +411,7 @@ class ShaderInfo:
rotation = placement[0:3, 0:3] rotation = placement[0:3, 0:3]
return rotation return rotation
def get_point_connection_rotation(self, def get_point_connection_rotation(self, point_connection: ifcopenshell.entity_instance) -> np.ndarray:
point_connection: ifcopenshell.entity_instance
) -> np.ndarray:
"""returns the rotation matrix of a structural point connection""" """returns the rotation matrix of a structural point connection"""
if point_connection.ConditionCoordinateSystem is not None: if point_connection.ConditionCoordinateSystem is not None:
placement = ifcopenshell.util.placement.get_axis2placement(point_connection.ConditionCoordinateSystem) placement = ifcopenshell.util.placement.get_axis2placement(point_connection.ConditionCoordinateSystem)
@@ -432,9 +420,7 @@ class ShaderInfo:
rotation = placement[0:3, 0:3] rotation = placement[0:3, 0:3]
return rotation return rotation
def get_curve_member_rotation(self, def get_curve_member_rotation(self, curve_member: ifcopenshell.entity_instance) -> np.ndarray:
curve_member: ifcopenshell.entity_instance
) -> np.ndarray:
"""returns the rotation matrix of a structural surface member""" """returns the rotation matrix of a structural surface member"""
z = curve_member.Axis.DirectionRatios z = curve_member.Axis.DirectionRatios
edge = curve_member.Representation.Representations[0].Items[0] edge = curve_member.Representation.Representations[0].Items[0]
@@ -445,18 +431,17 @@ class ShaderInfo:
rotation = placement[0:3, 0:3] rotation = placement[0:3, 0:3]
return rotation return rotation
def get_activity_transform_matrix(self, def get_activity_transform_matrix(
activity: ifcopenshell.entity_instance, self, activity: ifcopenshell.entity_instance, element_rotation_matrix: np.ndarray
element_rotation_matrix: np.ndarray
) -> np.ndarray: ) -> np.ndarray:
"provides the transformation matrix to convert between reference frames" "provides the transformation matrix to convert between reference frames"
global_or_local = activity.GlobalOrLocal global_or_local = activity.GlobalOrLocal
props = bpy.context.scene.BIMStructuralProperties props = bpy.context.scene.BIMStructuralProperties
reference_frame = props.reference_frame reference_frame = props.reference_frame
transform_matrix = np.eye(3) transform_matrix = np.eye(3)
if reference_frame == 'LOCAL_COORDS' and global_or_local != reference_frame: if reference_frame == "LOCAL_COORDS" and global_or_local != reference_frame:
transform_matrix = np.linalg.inv(element_rotation_matrix) transform_matrix = np.linalg.inv(element_rotation_matrix)
elif reference_frame == 'GLOBAL_COORDS' and global_or_local != reference_frame: elif reference_frame == "GLOBAL_COORDS" and global_or_local != reference_frame:
transform_matrix = element_rotation_matrix transform_matrix = element_rotation_matrix
return transform_matrix return transform_matrix
@@ -469,18 +454,14 @@ class ShaderInfo:
activity_list = value["activities"] activity_list = value["activities"]
if len(activity_list) == 0: if len(activity_list) == 0:
continue continue
blender_object = IfcStore.get_element(getattr(conn, 'GlobalId', None)) blender_object = IfcStore.get_element(getattr(conn, "GlobalId", None))
if blender_object.type == 'MESH': if blender_object.type == "MESH":
conn_location = blender_object.matrix_world @ blender_object.data.vertices[0].co conn_location = blender_object.matrix_world @ blender_object.data.vertices[0].co
rotation = self.get_point_connection_rotation(conn) rotation = self.get_point_connection_rotation(conn)
loads = self.get_point_loads_values(activity_list, rotation) loads = self.get_point_loads_values(activity_list, rotation)
self.get_point_shader_args(loads, conn_location, rotation) self.get_point_shader_args(loads, conn_location, rotation)
def get_point_shader_args(self, def get_point_shader_args(self, loads: Iterable, location: np.ndarray, rotation: np.ndarray) -> None:
loads: Iterable,
location: np.ndarray,
rotation: np.ndarray
) -> None:
"""get the args to the point shader""" """get the args to the point shader"""
location = np.array(location) location = np.array(location)
indices = [] indices = []
@@ -533,13 +514,10 @@ class ShaderInfo:
"shader": shader, "shader": shader,
"args": {"position": position, "coord": coords_for_shader}, "args": {"position": position, "coord": coords_for_shader},
"indices": indices, "indices": indices,
"uniforms": [["color", color],["spacing", 0.2]] "uniforms": [["color", color], ["spacing", 0.2]],
} }
) )
self.text_info.append( self.text_info.append({"position": location + d1, "text": f"{loads[i]:.2f} {self.force_unit}"})
{"position": location + d1,
"text": f'{loads[i]:.2f} {self.force_unit}'}
)
else: else:
d2 = d2 = direction_dict[key][1] d2 = d2 = direction_dict[key][1]
p1 = location - d2 p1 = location - d2
@@ -557,20 +535,19 @@ class ShaderInfo:
"shader": shader, "shader": shader,
"args": {"position": position, "coord": coords_for_shader}, "args": {"position": position, "coord": coords_for_shader},
"indices": indices, "indices": indices,
"uniforms": [["color", color]] "uniforms": [["color", color]],
} }
) )
self.text_info.append( self.text_info.append(
{"position": location +0.25*(d1 + d2), {"position": location + 0.25 * (d1 + d2), "text": f"{loads[i]:.2f} {self.moment_unit}"}
"text": f'{loads[i]:.2f} {self.moment_unit}'}
) )
def get_point_loads_values(self, def get_point_loads_values(
activity_list: list[tuple[ifcopenshell.entity_instance,float]], self, activity_list: list[tuple[ifcopenshell.entity_instance, float]], element_rotation_matrix: np.ndarray
element_rotation_matrix: np.ndarray) -> np.ndarray: ) -> np.ndarray:
"""returns a numpy array with the sum of the point load values""" """returns a numpy array with the sum of the point load values"""
result_list = np.zeros(6) result_list = np.zeros(6)
attr_list = ['ForceX','ForceY','ForceZ','MomentX','MomentY','MomentZ'] attr_list = ["ForceX", "ForceY", "ForceZ", "MomentX", "MomentY", "MomentZ"]
for item in activity_list: for item in activity_list:
activity = item[0] activity = item[0]
factor = item[1] factor = item[1]
@@ -587,7 +564,6 @@ class ShaderInfo:
return result_list return result_list
def get_linear_loads(self) -> None: def get_linear_loads(self) -> None:
position = [] position = []
indices = [] indices = []
@@ -604,22 +580,21 @@ class ShaderInfo:
if len(activity_list) == 0: if len(activity_list) == 0:
continue continue
blender_object = IfcStore.get_element(getattr(member, 'GlobalId', None)) blender_object = IfcStore.get_element(getattr(member, "GlobalId", None))
start_co = blender_object.matrix_world @ blender_object.data.vertices[0].co start_co = blender_object.matrix_world @ blender_object.data.vertices[0].co
end_co = blender_object.matrix_world @ blender_object.data.vertices[1].co end_co = blender_object.matrix_world @ blender_object.data.vertices[1].co
x_axis = Vector(end_co - start_co).normalized() x_axis = Vector(end_co - start_co).normalized()
z_direction = getattr(member, 'Axis') z_direction = getattr(member, "Axis")
# local coordinates # local coordinates
z_axis = Vector(getattr(z_direction, 'DirectionRatios', None)).normalized() z_axis = Vector(getattr(z_direction, "DirectionRatios", None)).normalized()
y_axis = z_axis.cross(x_axis).normalized() y_axis = z_axis.cross(x_axis).normalized()
z_axis = x_axis.cross(y_axis).normalized() z_axis = x_axis.cross(y_axis).normalized()
rotation = self.get_curve_member_rotation(member) rotation = self.get_curve_member_rotation(member)
props = bpy.context.scene.BIMStructuralProperties props = bpy.context.scene.BIMStructuralProperties
reference_frame = props.reference_frame reference_frame = props.reference_frame
is_local = reference_frame == 'LOCAL_COORDS' is_local = reference_frame == "LOCAL_COORDS"
x_match = abs(Vector((1, 0, 0)).dot(x_axis)) > 0.99 x_match = abs(Vector((1, 0, 0)).dot(x_axis)) > 0.99
y_match = abs(Vector((0, 1, 0)).dot(x_axis)) > 0.99 y_match = abs(Vector((0, 1, 0)).dot(x_axis)) > 0.99
z_match = abs(Vector((0, 0, 1)).dot(x_axis)) > 0.99 z_match = abs(Vector((0, 0, 1)).dot(x_axis)) > 0.99
@@ -628,10 +603,18 @@ class ShaderInfo:
"fy": y_axis if is_local else Vector((0, 1, 0)) if not y_match else Vector((1, 0, 1)), "fy": y_axis if is_local else Vector((0, 1, 0)) if not y_match else Vector((1, 0, 1)),
"fz": z_axis if is_local else Vector((0, 0, 1)) if not z_match else Vector((1, 1, 0)), "fz": z_axis if is_local else Vector((0, 0, 1)) if not z_match else Vector((1, 1, 0)),
"mx": z_axis - y_axis if is_local or x_match else Vector((1, 0, 0)).cross(x_axis), "mx": z_axis - y_axis if is_local or x_match else Vector((1, 0, 0)).cross(x_axis),
"my": z_axis if is_local else Vector((-1,0,1)) if y_match else Vector((0,1,0)).cross(x_axis).normalized(), "my": (
"mz": y_axis if is_local else Vector((-1,1,0)) if z_match else Vector((0,0,1)).cross(x_axis).normalized() z_axis
if is_local
else Vector((-1, 0, 1)) if y_match else Vector((0, 1, 0)).cross(x_axis).normalized()
),
"mz": (
y_axis
if is_local
else Vector((-1, 1, 0)) if z_match else Vector((0, 0, 1)).cross(x_axis).normalized()
),
} }
match_dict = {'fx': x_match or is_local, 'fy': y_match, 'fz': z_match} match_dict = {"fx": x_match or is_local, "fy": y_match, "fz": z_match}
member_length = Vector(end_co - start_co).length member_length = Vector(end_co - start_co).length
processed_loads = self.process_total_linear_loads(activity_list, rotation, member_length) processed_loads = self.process_total_linear_loads(activity_list, rotation, member_length)
linear_loads = processed_loads["linear loads"] linear_loads = processed_loads["linear loads"]
@@ -656,12 +639,12 @@ class ShaderInfo:
constant = linear_loads[key]["constant"] constant = linear_loads[key]["constant"]
direction = direction_dict[key] direction = direction_dict[key]
color_axis = (0, 0, 1, 1) color_axis = (0, 0, 1, 1)
if 'x' in key: if "x" in key:
color_axis = (1, 0, 0, 1) color_axis = (1, 0, 0, 1)
if 'y' in key: if "y" in key:
color_axis = (0, 1, 0, 1) color_axis = (0, 1, 0, 1)
if 'f' in key: if "f" in key:
unit = self.linear_force_unit unit = self.linear_force_unit
if match_dict[key]: if match_dict[key]:
shader = self.shader.new("PARALLEL DISTRIBUTED FORCE") shader = self.shader.new("PARALLEL DISTRIBUTED FORCE")
@@ -685,11 +668,13 @@ class ShaderInfo:
color.append(color_axis) color.append(color_axis)
x = current.x / member_length x = current.x / member_length
func = sin(x*3.1416)*sinus + (-4.*x*x+4.*x)*quadratic+constant+current.y func = sin(x * 3.1416) * sinus + (-4.0 * x * x + 4.0 * x) * quadratic + constant + current.y
if func: if func:
self.text_info.append( self.text_info.append(
{"position": -1*direction*func/maxforce + start_co + x_axis*current.x, {
"text": f'{func:.2f} {unit}'} "position": -1 * direction * func / maxforce + start_co + x_axis * current.x,
"text": f"{func:.2f} {unit}",
}
) )
position.append(positive) position.append(positive)
@@ -697,12 +682,8 @@ class ShaderInfo:
sin_quad_lin.append((sinus, quadratic, current.y + constant)) sin_quad_lin.append((sinus, quadratic, current.y + constant))
color.append(color_axis) color.append(color_axis)
indices.append((0 + counter, indices.append((0 + counter, 1 + counter, 2 + counter))
1 + counter, indices.append((3 + counter, 2 + counter, 1 + counter))
2 + counter))
indices.append((3 + counter,
2 + counter,
1 + counter))
if i == len(polyline) - 2: if i == len(polyline) - 2:
negative = -1 * direction + start_co + x_axis * nextitem.x negative = -1 * direction + start_co + x_axis * nextitem.x
positive = direction + start_co + x_axis * nextitem.x positive = direction + start_co + x_axis * nextitem.x
@@ -712,11 +693,15 @@ class ShaderInfo:
color.append(color_axis) color.append(color_axis)
x = nextitem.x / member_length x = nextitem.x / member_length
func = sin(x*3.1416)*sinus + (-4.*x*x+4.*x)*quadratic+constant+nextitem.y func = (
sin(x * 3.1416) * sinus + (-4.0 * x * x + 4.0 * x) * quadratic + constant + nextitem.y
)
if func: if func:
self.text_info.append( self.text_info.append(
{"position": -1*direction*func/maxforce + start_co + x_axis*nextitem.x, {
"text": f'{func:.2f} {unit}'} "position": -1 * direction * func / maxforce + start_co + x_axis * nextitem.x,
"text": f"{func:.2f} {unit}",
}
) )
position.append(positive) position.append(positive)
@@ -729,9 +714,13 @@ class ShaderInfo:
self.info.append( self.info.append(
{ {
"shader": shader, "shader": shader,
"args": {"position": position, "sin_quad_lin_forces": sin_quad_lin,"coord": coords_for_shader}, "args": {
"position": position,
"sin_quad_lin_forces": sin_quad_lin,
"coord": coords_for_shader,
},
"indices": indices, "indices": indices,
"uniforms": [["color", color_axis],["spacing", 0.2],["maxload",maxforce]] "uniforms": [["color", color_axis], ["spacing", 0.2], ["maxload", maxforce]],
} }
) )
position = [] position = []
@@ -741,11 +730,12 @@ class ShaderInfo:
for info in self.info: for info in self.info:
info["uniforms"][2][1] = maxforce info["uniforms"][2][1] = maxforce
def process_total_linear_loads(
def process_total_linear_loads(self, self,
activity_list: list[tuple[ifcopenshell.entity_instance, float]], activity_list: list[tuple[ifcopenshell.entity_instance, float]],
element_rotation_matrix: np.ndarray, element_rotation_matrix: np.ndarray,
member_length: float) -> ProcessedLoad: member_length: float,
) -> ProcessedLoad:
"""returns a dict with total values for applied loads in each direction """returns a dict with total values for applied loads in each direction
along with the maximum value for the loads in the member and the discrete loads applied along with the maximum value for the loads in the member and the discrete loads applied
@@ -772,7 +762,9 @@ class ShaderInfo:
final_list.append([member_length, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) final_list.append([member_length, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
elif len(final_list) > 0: elif len(final_list) > 0:
if final_list[0][0] and any(const+quad+sinus): #if first item location is not 0 append an item at the zero if final_list[0][0] and any(
const + quad + sinus
): # if first item location is not 0 append an item at the zero
final_list = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] + final_list final_list = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] + final_list
else: else:
del final_list[0] del final_list[0]
@@ -794,12 +786,16 @@ class ShaderInfo:
} }
max_load = 0 max_load = 0
for component, key in enumerate(keys): for component, key in enumerate(keys):
if(any([sinus[component], quad[component], const[component]]) or if any([sinus[component], quad[component], const[component]]) or any(
any(item for item in array[:,component+1])): item for item in array[:, component + 1]
):
for currentitem in final_list: for currentitem in final_list:
polyline.append([currentitem[0], currentitem[component + 1]]) polyline.append([currentitem[0], currentitem[component + 1]])
max_load = max(max_load,abs(sinus[component]+quad[component]+const[component]+currentitem[component+1])) max_load = max(
max_load,
abs(sinus[component] + quad[component] + const[component] + currentitem[component + 1]),
)
inner_dict = distributed_loads[key] inner_dict = distributed_loads[key]
inner_dict["constant"] = const[component] inner_dict["constant"] = const[component]
inner_dict["quadratic"] = quad[component] inner_dict["quadratic"] = quad[component]
@@ -807,10 +803,11 @@ class ShaderInfo:
inner_dict["polyline"] = polyline inner_dict["polyline"] = polyline
distributed_loads[key] = inner_dict distributed_loads[key] = inner_dict
return {"linear loads":distributed_loads, return {
"linear loads": distributed_loads,
"max linear load": max_load, "max linear load": max_load,
"discrete loads": loads_dict["point load configuration"]} "discrete loads": loads_dict["point load configuration"],
}
def getuniquepositionlist(self, load_config_list: list[list[LoadConfigItem]]) -> list[float]: def getuniquepositionlist(self, load_config_list: list[list[LoadConfigItem]]) -> list[float]:
"""return an ordereded list of unique locations based on the load configuration list """return an ordereded list of unique locations based on the load configuration list
@@ -843,10 +840,7 @@ class ShaderInfo:
result[i] = self.interp1d(value1, value2, pos) # interpolated [position, force_component] result[i] = self.interp1d(value1, value2, pos) # interpolated [position, force_component]
return result return result
def get_before_and_after(self, def get_before_and_after(self, pos: float, load_config_list: list[list[LoadConfigItem]]) -> dict[str, list[float]]:
pos: float,
load_config_list:list[list[LoadConfigItem]]
) -> dict[str,list[float]]:
"""get total values for forces and moments before and after the position """get total values for forces and moments before and after the position
example: example:
pos = 2.0 pos = 2.0
@@ -873,17 +867,17 @@ class ShaderInfo:
if pos < config[start]["pos"] or pos > config[end]["pos"]: if pos < config[start]["pos"] or pos > config[end]["pos"]:
break break
if config[start]["pos"] == pos: if config[start]["pos"] == pos:
if config[start]["descr"] in ['start','middle']: if config[start]["descr"] in ["start", "middle"]:
load_after += config[start]["load values"] load_after += config[start]["load values"]
elif config[start]["descr"] in ['end','middle']: elif config[start]["descr"] in ["end", "middle"]:
load_before += config[start]["load values"] load_before += config[start]["load values"]
elif config[end]["pos"] == pos: elif config[end]["pos"] == pos:
if config[end]["descr"] in ['start','middle']: if config[end]["descr"] in ["start", "middle"]:
load_after += config[end]["load values"] load_after += config[end]["load values"]
elif config[end]["descr"] in ['end','middle']: elif config[end]["descr"] in ["end", "middle"]:
load_before += config[end]["load values"] load_before += config[end]["load values"]
elif end - start == 1: elif end - start == 1:
@@ -891,15 +885,12 @@ class ShaderInfo:
load_after += self.interpolate(pos, config, start, end, "load values") load_after += self.interpolate(pos, config, start, end, "load values")
start += 1 start += 1
end -= 1 end -= 1
return_value = { return_value = {"before": load_before.tolist(), "after": load_after.tolist()}
"before": load_before.tolist(),
"after": load_after.tolist()
}
return return_value return return_value
def parse_linear_loads_to_dict(self, def parse_linear_loads_to_dict(
activity_list: list[tuple[ifcopenshell.entity_instance,float]], self, activity_list: list[tuple[ifcopenshell.entity_instance, float]], element_rotation_matrix: np.ndarray
element_rotation_matrix: np.ndarray) -> ParsedLoad: ) -> ParsedLoad:
""" """
get load list get load list
activity_list: list of IfcStructuralCurveAction or IfcStructuralCurveReaction activity_list: list of IfcStructuralCurveAction or IfcStructuralCurveReaction
@@ -938,7 +929,7 @@ class ShaderInfo:
def get_load_values(load, transform_matrix, factor=1.0): def get_load_values(load, transform_matrix, factor=1.0):
result = np.zeros(6) result = np.zeros(6)
keys = ['LinearForceX','LinearForceY','LinearForceZ','LinearMomentX','LinearMomentY','LinearMomentZ'] keys = ["LinearForceX", "LinearForceY", "LinearForceZ", "LinearMomentX", "LinearMomentY", "LinearMomentZ"]
for i, key in enumerate(keys): for i, key in enumerate(keys):
value = 0 if getattr(load, key, 0) is None else getattr(load, key, 0) value = 0 if getattr(load, key, 0) is None else getattr(load, key, 0)
result[i] += value * factor result[i] += value * factor
@@ -955,58 +946,48 @@ class ShaderInfo:
transform_6by6[3:6, 3:6] = transform_3by3 transform_6by6[3:6, 3:6] = transform_3by3
# values for linear loads # values for linear loads
if load.is_a('IfcStructuralLoadConfiguration'): if load.is_a("IfcStructuralLoadConfiguration"):
locations = getattr(load, 'Locations', []) locations = getattr(load, "Locations", [])
values = [l for l in getattr(load, 'Values', None) values = [l for l in getattr(load, "Values", None) if l.is_a() == "IfcStructuralLoadLinearForce"]
if l.is_a() == "IfcStructuralLoadLinearForce"
]
config_list = [] config_list = []
for i, l in enumerate(values): for i, l in enumerate(values):
load_values = get_load_values(l, transform_6by6, factor) load_values = get_load_values(l, transform_6by6, factor)
if i == 0: if i == 0:
descr = 'start' descr = "start"
elif i == len(values) - 1: elif i == len(values) - 1:
descr = 'end' descr = "end"
else: else:
descr = 'middle' descr = "middle"
config_list.append( config_list.append(
{"pos": locations[i][0]*unit_scale, {"pos": locations[i][0] * unit_scale, "descr": descr, "load values": load_values}
"descr": descr,
"load values":load_values}
) )
linear_load_configurations.append(config_list) linear_load_configurations.append(config_list)
# load configurations with point loads # load configurations with point loads
values = [l for l in getattr(load, 'Values', None) values = [l for l in getattr(load, "Values", None) if l.is_a() == "IfcStructuralLoadSingleForce"]
if l.is_a() == "IfcStructuralLoadSingleForce" attr_list = ["ForceX", "ForceY", "ForceZ", "MomentX", "MomentY", "MomentZ"]
]
attr_list = ['ForceX','ForceY','ForceZ','MomentX','MomentY','MomentZ']
config_list = [] config_list = []
for i, val in enumerate(values): for i, val in enumerate(values):
result_list = [0, 0, 0, 0, 0, 0] result_list = [0, 0, 0, 0, 0, 0]
for j, attr in enumerate(attr_list): for j, attr in enumerate(attr_list):
value = 0 if getattr(val, attr, 0) is None else getattr(val, attr, 0) value = 0 if getattr(val, attr, 0) is None else getattr(val, attr, 0)
result_list[j] += value * factor result_list[j] += value * factor
config_list.append( config_list.append({"pos": locations[i][0] * unit_scale, "values": result_list})
{"pos": locations[i][0]*unit_scale,
"values": result_list}
)
point_load_configurations.append(config_list) point_load_configurations.append(config_list)
else: else:
load_values = get_load_values(load, transform_6by6, factor) load_values = get_load_values(load, transform_6by6, factor)
if 'CONST' == getattr(activity, 'PredefinedType', None) or activity.is_a('IfcStructuralLinearAction'): if "CONST" == getattr(activity, "PredefinedType", None) or activity.is_a("IfcStructuralLinearAction"):
constant += load_values constant += load_values
elif 'PARABOLA' == getattr(activity, 'PredefinedType', None): elif "PARABOLA" == getattr(activity, "PredefinedType", None):
quadratic += load_values quadratic += load_values
elif 'SINUS' == getattr(activity, 'PredefinedType', None): elif "SINUS" == getattr(activity, "PredefinedType", None):
sinus += load_values sinus += load_values
return_value = { return_value = {
"constant force": constant.tolist(), "constant force": constant.tolist(),
"quadratic force": quadratic.tolist(), "quadratic force": quadratic.tolist(),
"sinus force": sinus.tolist(), "sinus force": sinus.tolist(),
"linear load configuration": linear_load_configurations, "linear load configuration": linear_load_configurations,
"point load configuration": point_load_configurations "point load configuration": point_load_configurations,
} }
return return_value return return_value
@@ -29,24 +29,26 @@ from mathutils import Vector, Matrix
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from bonsai.bim.module.structural.decorator import LoadsDecorator from bonsai.bim.module.structural.decorator import LoadsDecorator
class ShowLoads(bpy.types.Operator): class ShowLoads(bpy.types.Operator):
"""Draw decorations to show strucutural actions in 3d view""" """Draw decorations to show strucutural actions in 3d view"""
bl_idname = "bim.show_loads" bl_idname = "bim.show_loads"
bl_label = "Show loads in 3D View" bl_label = "Show loads in 3D View"
def modal(self, context, event): def modal(self, context, event):
if event.type == 'F5': if event.type == "F5":
LoadsDecorator.update() LoadsDecorator.update()
for area in context.screen.areas: for area in context.screen.areas:
if area.type == 'VIEW_3D': if area.type == "VIEW_3D":
area.tag_redraw() area.tag_redraw()
if event.type == 'ESC': if event.type == "ESC":
LoadsDecorator.uninstall() LoadsDecorator.uninstall()
for area in context.screen.areas: for area in context.screen.areas:
if area.type == 'VIEW_3D': if area.type == "VIEW_3D":
area.tag_redraw() area.tag_redraw()
return {'FINISHED'} return {"FINISHED"}
return {'PASS_THROUGH'} return {"PASS_THROUGH"}
def invoke(self, context, event): def invoke(self, context, event):
collection = bpy.data.collections["IfcStructuralItem"] collection = bpy.data.collections["IfcStructuralItem"]
@@ -60,10 +62,10 @@ class ShowLoads(bpy.types.Operator):
context.window.cursor_modal_restore() context.window.cursor_modal_restore()
context.window_manager.modal_handler_add(self) context.window_manager.modal_handler_add(self)
for area in context.screen.areas: for area in context.screen.areas:
if area.type == 'VIEW_3D': if area.type == "VIEW_3D":
area.tag_redraw() area.tag_redraw()
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}
class AddStructuralMemberConnection(bpy.types.Operator, tool.Ifc.Operator): class AddStructuralMemberConnection(bpy.types.Operator, tool.Ifc.Operator):
@@ -21,7 +21,12 @@ import bpy
import bonsai.tool as tool import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from bonsai.bim.prop import StrProperty, Attribute from bonsai.bim.prop import StrProperty, Attribute
from bonsai.bim.module.structural.data import StructuralLoadCasesData, StructuralLoadsData, BoundaryConditionsData, LoadGroupDecorationData from bonsai.bim.module.structural.data import (
StructuralLoadCasesData,
StructuralLoadsData,
BoundaryConditionsData,
LoadGroupDecorationData,
)
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -34,14 +39,17 @@ from bpy.props import (
CollectionProperty, CollectionProperty,
) )
def get_load_groups_to_show(self, context): def get_load_groups_to_show(self, context):
if not LoadGroupDecorationData.is_loaded: if not LoadGroupDecorationData.is_loaded:
LoadGroupDecorationData.load() LoadGroupDecorationData.load()
return LoadGroupDecorationData.data["load groups to show"] return LoadGroupDecorationData.data["load groups to show"]
def update_activity_type(self, context): def update_activity_type(self, context):
LoadGroupDecorationData.is_loaded = False LoadGroupDecorationData.is_loaded = False
def get_applicable_structural_load_types(self, context): def get_applicable_structural_load_types(self, context):
if not StructuralLoadCasesData.is_loaded: if not StructuralLoadCasesData.is_loaded:
StructuralLoadCasesData.load() StructuralLoadCasesData.load()
@@ -162,13 +170,22 @@ class BIMStructuralProperties(PropertyGroup):
show_loads: BoolProperty(name="Show Loads", default=False) show_loads: BoolProperty(name="Show Loads", default=False)
update_load_repr: BoolProperty(name="Update Load Representation", default=False) update_load_repr: BoolProperty(name="Update Load Representation", default=False)
enable_repr_auto_update: BoolProperty(name="Auto Update", 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"), reference_frame: EnumProperty(
("LOCAL_COORDS","Local","Show loads in local reference frame")], name= "Reference Frame") items=[
activity_type: EnumProperty(items=[("Action","Actions","Show actions loads"), ("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"), ("External Reaction", "External Reactions", "Show reactions on boundary conditions"),
("Internal Reactions","Internal Reactions","Show internal reactions on members")], ("Internal Reactions", "Internal Reactions", "Show internal reactions on members"),
],
name="Activity Type", name="Activity Type",
update=update_activity_type) update=update_activity_type,
)
load_group_to_show: EnumProperty(items=get_load_groups_to_show, name="Load Groups") load_group_to_show: EnumProperty(items=get_load_groups_to_show, name="Load Groups")
@@ -17,12 +17,20 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import gpu import gpu
from typing import Literal
class DecorationShader: class DecorationShader:
"shader for the load decorations" "shader for the load decorations"
def __init__(self): def __init__(self):
pass pass
def new(self, pattern: str) -> gpu.types.GPUShader:
def new(self, pattern: Literal["PERPENDICULAR DISTRIBUTED FORCE",
"PARALLEL DISTRIBUTED FORCE",
"DISTRIBUTED MOMENT",
"SINGLE FORCE",
"SINGLE MOMENT",
"PLANAR LOAD",]) -> gpu.types.GPUShader:
"""pattern: string description of the desired shader """pattern: string description of the desired shader
Possible values Possible values
PERPENDICULAR DISTRIBUTED FORCE: pattern for distributed force PERPENDICULAR DISTRIBUTED FORCE: pattern for distributed force
@@ -34,21 +42,24 @@ class DecorationShader:
SINGLE MOMENT: pattern for single moments SINGLE MOMENT: pattern for single moments
PLANAR LOAD: pattern for planar loads PLANAR LOAD: pattern for planar loads
""" """
valid_patterns = {"PERPENDICULAR DISTRIBUTED FORCE", valid_patterns = {
"PERPENDICULAR DISTRIBUTED FORCE",
"PARALLEL DISTRIBUTED FORCE", "PARALLEL DISTRIBUTED FORCE",
"DISTRIBUTED MOMENT", "DISTRIBUTED MOMENT",
"SINGLE FORCE", "SINGLE FORCE",
"SINGLE MOMENT", "SINGLE MOMENT",
"PLANAR LOAD" "PLANAR LOAD",
} }
if pattern not in valid_patterns: if pattern not in valid_patterns:
raise ValueError("""pattern must be one of: raise ValueError(
"""pattern must be one of:
PERPENDICULAR DISTRIBUTED FORCE PERPENDICULAR DISTRIBUTED FORCE
PARALLEL DISTRIBUTED FORCE, PARALLEL DISTRIBUTED FORCE,
DISTRIBUTED MOMENT, DISTRIBUTED MOMENT,
SINGLE FORCE, SINGLE FORCE,
SINGLE MOMENT, SINGLE MOMENT,
PLANAR LOAD""") PLANAR LOAD"""
)
if "DISTRIBUTED" in pattern.upper(): if "DISTRIBUTED" in pattern.upper():
shader = self.get_linear_shader(pattern) shader = self.get_linear_shader(pattern)
return shader return shader
@@ -59,28 +70,30 @@ class DecorationShader:
shader = self.get_planar_shader() shader = self.get_planar_shader()
return shader return shader
def get_linear_shader(self, pattern: str) -> gpu.types.GPUShader: def get_linear_shader(self, pattern: Literal["PERPENDICULAR DISTRIBUTED FORCE",
"PARALLEL DISTRIBUTED FORCE",
"DISTRIBUTED MOMENT"]) -> gpu.types.GPUShader:
"""pattern: type of pattern """pattern: type of pattern
PERPENDICULAR DISTRIBUTED FORCE PERPENDICULAR DISTRIBUTED FORCE
PARALLEL DISTRIBUTED FORCE, PARALLEL DISTRIBUTED FORCE,
DISTRIBUTED MOMENT, DISTRIBUTED MOMENT,
""" """
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "forces") vert_out.smooth("VEC3", "forces")
vert_out.smooth('VEC3', "co") vert_out.smooth("VEC3", "co")
shader_info = gpu.types.GPUShaderCreateInfo() shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix") shader_info.push_constant("MAT4", "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color") shader_info.push_constant("VEC4", "color")
shader_info.push_constant('FLOAT', "spacing") shader_info.push_constant("FLOAT", "spacing")
shader_info.push_constant('FLOAT', "maxload") shader_info.push_constant("FLOAT", "maxload")
shader_info.vertex_in(0, 'VEC3', "position") shader_info.vertex_in(0, "VEC3", "position")
shader_info.vertex_in(1, 'VEC3', "sin_quad_lin_forces") shader_info.vertex_in(1, "VEC3", "sin_quad_lin_forces")
shader_info.vertex_in(2, 'VEC3', "coord") shader_info.vertex_in(2, "VEC3", "coord")
shader_info.vertex_out(vert_out) shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor") shader_info.fragment_out(0, "VEC4", "FragColor")
shader_info.vertex_source( shader_info.vertex_source(
"void main()" "void main()"
@@ -98,11 +111,9 @@ class DecorationShader:
"float x = co.x;" "float x = co.x;"
"float y = co.y;" "float y = co.y;"
"float abs_y = abs(y);" "float abs_y = abs(y);"
"float a = abs(mod(x,spacing)-0.5*spacing)*5.0;" "float a = abs(mod(x,spacing)-0.5*spacing)*5.0;"
"float b = step(a,abs_y)*(step(abs_y,1.2*spacing));" "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 c = step(0.8*spacing,mod(x+0.4*spacing,spacing))*(step(1.2*spacing,abs_y));"
"float sinvalue = forces.x;" "float sinvalue = forces.x;"
"float quadraticvalue = forces.y;" "float quadraticvalue = forces.y;"
"float linearvalue = forces.z;" "float linearvalue = forces.z;"
@@ -111,7 +122,6 @@ class DecorationShader:
"+(-4.*x*x+4.*x)*quadraticvalue" "+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;" "+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);" "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 top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(top+b+c,0.0,0.9)*mask;" "float d = clamp(top+b+c,0.0,0.9)*mask;"
"if (d == 0.0) discard;" "if (d == 0.0) discard;"
@@ -126,7 +136,6 @@ class DecorationShader:
"float y = co.y;" "float y = co.y;"
"float x = step(0.,y)*(co.z-co.x)+step(y,0.)*(co.x);" "float x = step(0.,y)*(co.z-co.x)+step(y,0.)*(co.x);"
"float abs_y = abs(y);" "float abs_y = abs(y);"
"float a = abs(mod(abs_y,spacing)-0.5*spacing)*5.0;" "float a = abs(mod(abs_y,spacing)-0.5*spacing)*5.0;"
"float a2 = mod(x,3.0*spacing);" "float a2 = mod(x,3.0*spacing);"
"float b = step(a,a2)*step(a2,1.2*spacing);" "float b = step(a,a2)*step(a2,1.2*spacing);"
@@ -140,7 +149,6 @@ class DecorationShader:
"+(-4.*x*x+4.*x)*quadraticvalue" "+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;" "+linearvalue)/maxload;"
"float mask = step(0.,y)*step(y,f)+step(y,0.)*step(f,y);" "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 top = step(abs(y-f),0.2*1.2*spacing);"
"float d = clamp(top+b+c,0.0,0.9)*mask;" "float d = clamp(top+b+c,0.0,0.9)*mask;"
"if (d == 0.0) discard;" "if (d == 0.0) discard;"
@@ -159,16 +167,13 @@ class DecorationShader:
"float abs_y = abs(y);" "float abs_y = abs(y);"
"vec2 st = vec2(1.9*x,y);" "vec2 st = vec2(1.9*x,y);"
"vec2 orig = vec2(0.,0.);" "vec2 orig = vec2(0.,0.);"
"float circ = step(distance(st,orig),0.33)*step(0.27,distance(st,orig));" "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 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 circ_arrow = step(st.x,4.0*st.y-0.75)*step(0.25*st.y-0.34,st.x)*(1.-tri_mask);"
"float circmask = step(distance(st,orig),0.1)+step(0.5,distance(st,orig))+step(st.x,0.);" "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 = 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 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.);" "float d = clamp(circmask*(body+body_arrow)+circ_arrow+circ*tri_mask,0.,1.);"
"float sinvalue = forces.x;" "float sinvalue = forces.x;"
"float quadraticvalue = forces.y;" "float quadraticvalue = forces.y;"
"float linearvalue = forces.z;" "float linearvalue = forces.z;"
@@ -177,9 +182,7 @@ class DecorationShader:
"+(-4.*x*x+4.*x)*quadraticvalue" "+(-4.*x*x+4.*x)*quadraticvalue"
"+linearvalue)/maxload;" "+linearvalue)/maxload;"
"float mask = step(0.,co.y)*step(co.y,f)+step(co.y,0.)*step(f,co.y);" "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 top = step(abs(co.y-f),0.2*1.2*spacing);"
"d = clamp(top+d,0.0,0.9)*mask;" "d = clamp(top+d,0.0,0.9)*mask;"
"if (d == 0.0) discard;" "if (d == 0.0) discard;"
"FragColor = vec4(color.xyz,d*color.w);" "FragColor = vec4(color.xyz,d*color.w);"
@@ -191,30 +194,26 @@ class DecorationShader:
del shader_info del shader_info
return shader return shader
def get_point_shader(self, pattern: str) -> gpu.types.GPUShader: def get_point_shader(self, pattern: Literal["SINGLE FORCE","SINGLE MOMENT"]) -> gpu.types.GPUShader:
"""param: pattern: type of pattern """param: pattern: type of pattern
SINGLE FORCE, SINGLE FORCE,
SINGLE MOMENT""" SINGLE MOMENT"""
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "co") vert_out.smooth("VEC3", "co")
shader_info = gpu.types.GPUShaderCreateInfo() shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix") shader_info.push_constant("MAT4", "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color") shader_info.push_constant("VEC4", "color")
shader_info.push_constant('FLOAT', "spacing") shader_info.push_constant("FLOAT", "spacing")
shader_info.vertex_in(0, 'VEC3', "position") shader_info.vertex_in(0, "VEC3", "position")
shader_info.vertex_in(1, 'VEC3', "coord") shader_info.vertex_in(1, "VEC3", "coord")
shader_info.vertex_out(vert_out) shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor") shader_info.fragment_out(0, "VEC4", "FragColor")
shader_info.vertex_source( shader_info.vertex_source(
"void main()" "void main()" "{" " gl_Position = viewProjectionMatrix * vec4(position, 1.0f);" " co = coord;" "}"
"{"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
" co = coord;"
"}"
) )
if pattern == "SINGLE FORCE": if pattern == "SINGLE FORCE":
@@ -250,25 +249,21 @@ class DecorationShader:
def get_planar_shader(self) -> gpu.types.GPUShader: def get_planar_shader(self) -> gpu.types.GPUShader:
"""shader for planar loads""" """shader for planar loads"""
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC3', "co") vert_out.smooth("VEC3", "co")
shader_info = gpu.types.GPUShaderCreateInfo() shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.push_constant('MAT4', "viewProjectionMatrix") shader_info.push_constant("MAT4", "viewProjectionMatrix")
shader_info.push_constant('VEC4', "color") shader_info.push_constant("VEC4", "color")
shader_info.push_constant('FLOAT', "spacing") shader_info.push_constant("FLOAT", "spacing")
shader_info.vertex_in(0, 'VEC3', "position") shader_info.vertex_in(0, "VEC3", "position")
shader_info.vertex_in(1, 'VEC3', "coord") shader_info.vertex_in(1, "VEC3", "coord")
shader_info.vertex_out(vert_out) shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', "FragColor") shader_info.fragment_out(0, "VEC4", "FragColor")
shader_info.vertex_source( shader_info.vertex_source(
"void main()" "void main()" "{" " gl_Position = viewProjectionMatrix * vec4(position, 1.0f);" " co = coord;" "}"
"{"
" gl_Position = viewProjectionMatrix * vec4(position, 1.0f);"
" co = coord;"
"}"
) )
shader_info.fragment_source( shader_info.fragment_source(
@@ -277,14 +272,10 @@ class DecorationShader:
"float x = co.x;" "float x = co.x;"
"float y = co.y;" "float y = co.y;"
"float abs_y = abs(y);" "float abs_y = abs(y);"
"float a = abs(mod(x,spacing)-0.5*spacing)*5.0;" "float a = abs(mod(x,spacing)-0.5*spacing)*5.0;"
"float b = step(a,abs_y)*(step(abs_y,1.2*spacing));" "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 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 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 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);" "float d = clamp(0.2*y+(top+b+c)*mask,0.0,0.4);"
"FragColor = vec4(color.xyz,d*color.w);" "FragColor = vec4(color.xyz,d*color.w);"
@@ -444,6 +444,7 @@ class BIM_UL_structural_activities(UIList):
row.label(text=item.name) row.label(text=item.name)
row.label(text=item.applied_load_class) row.label(text=item.applied_load_class)
class BIM_PT_show_structural_activities(Panel): class BIM_PT_show_structural_activities(Panel):
bl_label = "Show Loads" bl_label = "Show Loads"
bl_idname = "BIM_PT_show_structural_activities" bl_idname = "BIM_PT_show_structural_activities"