# Duality

James Clerk Maxwell used the principle of duality in projective geometry to express the reciprocal relationship between the form and force diagrams in the context of graphic statics (1). The 2D form and force diagrams have a dual relationship with each other; the points and lines of one diagram is mapped to the lines and points of the other. The mapping of one geometric object in one diagram to a different geometric object in the other diagram can be explained by Maxwell’s observation that 2D form and force diagrams are projections of plane-faced three-dimensional polyhedrons (2, 3).

The same relationship is true for any $$n$$-dimensional reciprocal form and force diagrams: n-dimensional reciprocal diagrams are projections of $$(n+1)$$-dimensional stress functions (4, 5) Based on this principle, three-dimensional form and force diagrams can be defined as projections of four-dimensional stress functions. The duality relationships between the elements of $$n$$-dimensional structures and the $$(n+1)$$-dimension of the corresponding stress functions are summarised in the figure below. Summary of the duality relationships between the elements of $$n$$-dimensional structures and the $$(n+1)$$-dimension of the corresponding stress functions (force diagrams) (after Figure 1 of 6).

## Face dual

The reciprocal diagrams of 2D structures have a “face dual” relationship, where the vertices one diagram corresponds to the faces of the other diagram. “Face dual” is equivalent to the compas.datastructures.mesh_dual. The steps of the “face_dual” constructor is graphically summarised below.

## Cell dual

The reciprocal diagrams of 3D structures have a “cell dual” relationship, where the vertices of one diagram corresponds to the cells of the other diagram. A polyhedral cell and the corresponding form diagram have a cell dual relationship; the polyhedral cell and its faces correspond to a vertex and edges of the form diagram, respectively. The “cell dual” constructor is implemented as compas_3gs.algorithms.volmesh_dual_volmesh(). The steps of the “cell_dual” constructor is graphically summarised below.

## From volmesh to volmesh

Using the “cell dual” relationship, a dual volmesh can be constructed from an existing volmesh.

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import compas

from compas_rhino.helpers import volmesh_from_polysurfaces

from compas_3gs.diagrams import ForceVolMesh
from compas_3gs.diagrams import FormVolMesh

from compas_3gs.algorithms import volmesh_dual_volmesh

from compas_3gs.rhino import draw_cell_labels
from compas_3gs.rhino import draw_directed_hf_and_uv

from compas_3gs.utilities import get_index_colordict

try:
import rhinoscriptsyntax as rs
except ImportError:
compas.raise_if_ironpython()

__author__     = 'Juney Lee'
__email__      = 'juney.lee@arch.ethz.ch'

# ------------------------------------------------------------------------------
# 1. make vomesh from rhino polysurfaces (force diagram)
# ------------------------------------------------------------------------------
layer = 'volmesh'

guids = rs.GetObjects("select polysurfaces", filter=rs.filter.polysurface)
rs.HideObjects(guids)

volmesh       = ForceVolMesh()
volmesh       = volmesh_from_polysurfaces(volmesh, guids)
volmesh.layer = layer
volmesh.attributes['name'] = layer

# ------------------------------------------------------------------------------
# 2. make dual volmesh (form diagram)
# ------------------------------------------------------------------------------
dual_layer   = 'dual_volmesh'

dual_volmesh = volmesh_dual_volmesh(volmesh, cls=FormVolMesh)
dual_volmesh.layer = dual_layer
dual_volmesh.attributes['name'] = dual_layer

# move dual_network
offset = 3
x_move = dual_volmesh.bounding_box() * offset
for vkey in dual_volmesh.vertex:
dual_volmesh.vertex[vkey]['x'] += x_move

# ------------------------------------------------------------------------------
# 3. visualise diagrams
# ------------------------------------------------------------------------------

# draw volmesh cell labels and dual_volmesh vertex labels
cell_c_dict = get_index_colordict(list(volmesh.cell.keys()))

# draw volmesh
volmesh.draw()
draw_cell_labels(volmesh, color=cell_c_dict)

# draw dual volmesh
dual_volmesh.draw_faces()
dual_volmesh.draw_vertex_labels(color=cell_c_dict)

# draw directed volmesh halffaces and directed dual_volmesh edges
uv_c_dict = get_index_colordict(list(dual_volmesh.edges()))

face_normal_scale = 1.0
draw_directed_hf_and_uv(volmesh,
dual_volmesh,
uv_color=uv_c_dict,
scale=face_normal_scale)


Note

An input volmesh with a minimum of four cells is required for the construction of a dual volmesh (a tetrahedron). In another words, in order for a volmesh to have a dual volmesh, the volmesh needs to have an interior vertex.

## From volmesh to network

If the initial volmesh is interpreted as a polyhedral force diagram, then the edges of the dual_volmesh then represents the polyhedral form diagram. The topological information (vertices, edges, faces and cells) of the dual_volmesh is intrinsically embedded in the volmesh. From a practical point of view, the edges of the form diagram are typically of interest to the designer. Therefore, not all information need to be constantly duplicated during the design process. The corresponding form diagram of a polyhedral force diagram modelled as a volmesh, can equivalently be represented by a network. When needed, additional topological information about the network can be retrieved from the volmesh. For example, the face and cell information of the network can be computed from the topological information of the corresponding interior vertex of the volmesh.

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import compas

from compas_rhino.helpers import volmesh_from_polysurfaces

from compas_3gs.diagrams import ForceVolMesh
from compas_3gs.diagrams import FormNetwork

from compas_3gs.algorithms import volmesh_dual_network

from compas_3gs.rhino import draw_directed_hf_and_uv
from compas_3gs.rhino import draw_cell_labels

from compas_3gs.utilities import get_index_colordict

try:
import rhinoscriptsyntax as rs
except ImportError:
compas.raise_if_ironpython()

__author__     = 'Juney Lee'
__email__      = 'juney.lee@arch.ethz.ch'

# ------------------------------------------------------------------------------
# 1. make vomesh from rhino polysurfaces (force diagram)
# ------------------------------------------------------------------------------
layer = 'volmesh'

guids = rs.GetObjects("select polysurfaces", filter=rs.filter.polysurface)
rs.HideObjects(guids)

volmesh       = ForceVolMesh()
volmesh       = volmesh_from_polysurfaces(volmesh, guids)
volmesh.layer = layer
volmesh.attributes['name'] = layer

# ------------------------------------------------------------------------------
# 2. make dual network (form diagram)
# ------------------------------------------------------------------------------
dual_layer   = 'dual_network'

dual_network = volmesh_dual_network(volmesh, cls=FormNetwork)
dual_network.layer = dual_layer
dual_network.attributes['name'] = dual_layer

# move dual_network
offset = 3
x_move = dual_network.bounding_box() * offset
for vkey in dual_network.vertex:
dual_network.vertex[vkey]['x'] += x_move

# ------------------------------------------------------------------------------
# 3. visualise diagrams
# ------------------------------------------------------------------------------

# draw directed volmesh halffaces and directed dual_volmesh edges
uv_c_dict = get_index_colordict(list(dual_network.edges()))

face_normal_scale = 1.0
volmesh.draw_edges()
draw_directed_hf_and_uv(volmesh,
dual_network,
uv_color=uv_c_dict,
scale=face_normal_scale)

# draw volmesh cell labels and dual network vertex labels
cell_c_dict = get_index_colordict(list(volmesh.cell.keys()))

draw_cell_labels(volmesh, color=cell_c_dict)
dual_network.draw_vertex_labels(color=cell_c_dict)