You 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 three weeks on cable nets.
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 dictionary it 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)
All these visualisation functionalities and more are already available in compas_fofin's CablenetArtist.
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 A3_visualization.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__':deftest_load(mesh):# placeholder only to have something to visualise# for all vertices that are freefor key, attr in mesh.vertices_where({'is_anchor': False}, True): attr['pz']=-0.05deftest_residual(mesh):# placeholder only to have something to visualise# for all vertices that are freefor key, attr in mesh.vertices_where({'is_anchor': False}, True): attr['rz']=0.1# ==============================================================================# Initialise# ============================================================================== HERE = os.path.dirname(__file__) DATA = os.path.abspath(os.path.join(HERE, '..', 'data')) FILE_I = os.path.join(DATA, 'cablenet_fofin.json')# create the mesh from imported geometry mesh = Mesh.from_json(FILE_I)test_load(mesh)test_residual(mesh)# ==============================================================================# Visualize# ============================================================================== baselayer ="CSD2::A3::Visualization" 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})}) 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, scale=10)
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:
from compas.datastructures import Meshfrom compas_rhino.artists import MeshArtistfrom A3_visualization import draw_reactions from A3_visualization import draw_residuals from A3_visualization import draw_forces from A3_visualization import draw_loads
For importing the function in another file, the A3_visualisation.py file must be in the same subfolder! So just copy the file over into the next week's session folder.
This can be avoided when working with your own package and modules, however, this is outside the scope of this course.