C2.3 Visualisation

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

def draw_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

def draw_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))
        if length_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

def draw_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 * f
        if 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

def draw_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 os
    from compas.datastructures import Mesh

    from 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 loads
    for 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:

from C2_3_visualisation import draw_reactions
from C2_3_visualisation import draw_residuals
from C2_3_visualisation import draw_forces
from C2_3_visualisation import draw_loads

For importing the function in another file, the C2.3_visualisation.py file must be in the same subfolder!

This can be avoided when working with your own package and modules, however, this is outside the scope of this workshop.

Last updated