Visualise the reaction, residual and edge forces and external loads. (by Lotte)
Objectives
We will learn how to visualise the reaction, residual and edge forces and external loads in Rhino with separate functions. These functions will not only be used within this script but also imported for all other scripts throughout the form-finding process of this workshop.
Visualisation Functions
The visualisation functions all follow a similar pattern: we build a list of dictionaries for each vertex/edge that we then hand over to a compas_rhino function that does the rest for us.
Check the documentation e.g. for draw_lines to find out what the dictionary expects.
Reaction Forces
defdraw_reactions(mesh,baselayer,color=(0,255,0),scale=1):"""Visualise the reaction forces in rhino as green arrows. """ lines = []for key in mesh.vertices_where({'is_anchor': True}): start = mesh.vertex_attributes(key, 'xyz') residual = mesh.vertex_attributes(key, ['rx', 'ry', 'rz']) end =add_vectors(start, scale_vector(residual, -scale)) lines.append({'start': start,'end': end,'name': "{}.reaction.{}".format(key, round(length_vector(residual), 3)),'arrow': 'end','color': color }) layer = baselayer+"::Reactions" compas_rhino.draw_lines(lines, layer=layer, clear=True)
Residual Forces
defdraw_residuals(mesh,baselayer,color=(0,255,255),tol=0.001,scale=1):"""Visualise the residual forces in rhino as cyan arrows. """ lines = []for key in mesh.vertices_where({'is_anchor': False}): start = mesh.vertex_attributes(key, 'xyz') residual = mesh.vertex_attributes(key, ['rx', 'ry', 'rz']) end =add_vectors(start, scale_vector(residual, scale))iflength_vector(residual)< tol:continue lines.append({'start': start,'end': end,'name': "{}.residual.{}".format(key, round(length_vector(residual), 3)),'arrow': 'end','color': color}) layer = baselayer+"::Residuals" compas_rhino.draw_lines(lines, layer=layer, clear=True)
Edge Forces
defdraw_forces(mesh,baselayer,color=(255,0,0),scale=0.1,tol=0.001):"""Visualise the edge forces in rhino as red-gradient pipes. """ f_max = []for edge in mesh.edges(): f = mesh.edge_attribute(edge, 'f') f_max.append(abs(f)) f_max =max(f_max) cylinders = []for edge in mesh.edges(): f = mesh.edge_attribute(edge, 'f') radius = scale * fif radius < tol:continue start_end = mesh.edge_coordinates(*edge) cylinders.append({'start': start_end[0],'end': start_end[1],'name': "{}.force.{}".format(edge, round(f, 3)),'radius': radius,'color': i_to_red(abs(f)/f_max)}) layer = baselayer+"::Forces" compas_rhino.draw_cylinders(cylinders, layer=layer, clear=True)
External Loads
defdraw_loads(mesh,baselayer,color=(0,255,0),scale=1):"""Visualise the external loads in rhino as green arrows. """ lines = []for key in mesh.vertices_where({'is_anchor': False}): start = mesh.vertex_attributes(key, 'xyz') load = mesh.vertex_attributes(key, ['px', 'py', 'pz']) end =add_vectors(start, scale_vector(load, scale)) lines.append({'start': start,'end': end,'name': "{}.load.{}".format(key, round(length_vector(load), 3)),'arrow': 'end','color': color}) layer = baselayer+"::Loads" compas_rhino.draw_lines(lines, layer=layer, clear=True)
Main
The if __name__ == '__main__': checks if the file itself is run or not. So everything inside will only be executed if we run the C2.3_visualisation.py file itself but to if we import its functions from another script. Everything inside serves for testing the functions.
So inside the main body, we create some artificial load and reaction force that we then visualise together with the form found mesh that we import from the data folder.
# ==============================================================================# Main# ==============================================================================if__name__=='__main__':import osfrom compas.datastructures import Meshfrom compas_rhino.artists import MeshArtist# ==============================================================================# Initialise# ============================================================================== HERE = os.path.dirname(__file__) DATA = os.path.abspath(os.path.join(HERE, '..', 'data')) FILE_I = os.path.join(DATA, 'cablemesh_fofin_simple.json')# create the mesh from imported geometry mesh = Mesh.from_json(FILE_I)# add artificial residual and external loadsfor key, attr in mesh.vertices_where({'is_anchor': False}, True): attr['pz']=-0.2 attr['rz']=0.2# ==============================================================================# Visualize# ============================================================================== baselayer ="DF21_C2::03_Visualisation" artist =MeshArtist(mesh, layer=baselayer+"::Mesh") artist.clear_layer() artist.draw_vertices(color={vertex: (255, 0, 0) for vertex in mesh.vertices_where({'is_anchor': True})}) # noqa: E501
artist.draw_edges() artist.draw_faces()draw_reactions(mesh, baselayer=baselayer)draw_residuals(mesh, baselayer=baselayer)draw_forces(mesh, baselayer=baselayer)draw_loads(mesh, baselayer=baselayer)
Access from Other Scripts
As mentioned, the visualisation functions can also be accessed from other Python scripts. This can be done with the import keyword, same as we for example import a Mesh or MeshArtist from the COMPAS framework: