4. Create ForceDiagram

4.a. Create 'ForceDiagram', actually only the Dual

Once the FormDiagram has been successfully created, the ForceDiagram can be created. It is the "centroidal dual" diagram of the FormDiagram.

This is not actually the reciprocal force diagram, it is only the topological dual. For simplicity, we call this the force diagram already here. Be clear of the name differences that here are overlapping because of its computational implementation.

Explanation: Centroidal Dual

The Force Diagram is the dual of the Form Diagram, in the sense that both diagrams have the same number of edges and that vertices in one diagram correspond to faces in the other, and vice versa.

Initially, the Force Diagram is created as the "centroidal dual" of the Form Diagram. This means that the geometry of the Force Diagram is defined by placing its vertices at the centroids of their corresponding faces in the Form Diagram.

Explanation: Reciprocal Diagrams

In order for the Form and Force Diagram to describe the distribution of horizontal thrust in a three-dimensional network of compression forces in equilibrium with vertical loads applied to its nodes, they need to be not only dual, but also reciprocal.

Two diagrams are reciprocal if they are dual, and if their corresponding edges are at a constant angle with each other. Typically, corresponding edges are required to be parallel, or perpendicular, but any other constant angle is sufficient as well.

In RV2, the Form and Force Diagram are considered reciprocal if corresponding edges are perpendicular.

This will only be the case after our next step 5. Horizontal Equilibirum.

Explanation: Horizontal Forces

Once the Form and Force Diagram are reciprocal they describe the horizontal equilibrium of the corresponding three-dimensional force network. The edges of the Form Diagram define the directions and points of application of the forces, whereas the edges of the Force Diagram define the distribution of force magnitudes along those directions.

The magnitudes of horizontal forces are equal to the lengths of the edges in the Force Diagram, multiplied with a scaling factor.

Back to our example:

from compas_rv2.datastructures import ForceDiagram
from compas_rv2.rhino import ForceArtist

# ==============================================================================
#  Create Force
# ==============================================================================
force = ForceDiagram.from_formdiagram(form)

# ==============================================================================
#  Visualization
# ==============================================================================
forceartist = ForceArtist(force, layer="CSD2::force")
forceartist.clear_layer()
forceartist.draw()

4.b. Translate ForceDiagram

The ForceDiagram can be translated so that it doesn't overlap with the FormDiagram.

# transform the force diagram for better visualization
bbox_form = form.bounding_box_xy()
bbox_force = force.bounding_box_xy()
xmin_form, xmax_form = bbox_form[0][0], bbox_form[1][0]
xmin_force, _ = bbox_force[0][0], bbox_force[1][0]
ymin_form, ymax_form = bbox_form[0][1], bbox_form[3][1]
ymin_force, ymax_force = bbox_force[0][1], bbox_force[3][1]
y_form = ymin_form + 0.5 * (ymax_form - ymin_form)
y_force = ymin_force + 0.5 * (ymax_force - ymin_force)
dx = 1.3 * (xmax_form - xmin_form) + (xmin_form - xmin_force)
dy = y_form - y_force
force.transform(Translation.from_vector([dx, dy, 0]))

4.c. Show Angle Deviation

Though ForceDiagram is created now, the corresponding edges in the FormDiagram and ForceDiagram are not perpendicular to each other. Thus, the ForceDiagram cannot represent the horizontal equilibrium yet.

force.update_angle_deviations()

The method update_angle_deviations will compute the angle deviation with the corresponding edge in the Force and FormDiagram and update the edge attribute _a in both diagrams, which means the deviation angle.

Then, the edge attribute _a can be visualized by artist.draw_edgelabels.

def draw_angle_deviations(diagram, diagram_artist, tol_angle=5):
    edges = list(diagram.edges())
    angles = diagram.edges_attribute('_a', keys=edges)
    amin = min(angles)
    amax = max(angles)
    if (amax - amin)**2 > 0.001**2:
        text = {}
        color = {}
        for edge, angle in zip(edges, angles):
            if angle > tol_angle:
                text[edge] = "{:.0f}".format(angle)
                color[edge] = i_to_rgb((angle - amin) / (amax - amin))
        diagram_artist.draw_edgelabels(text, color)

draw_angle_deviations(force, forceartist)
draw_angle_deviations(form, formartist)

Last updated