# Planarisation

At any point during the design process, the faces of polyhedral cells or multi-cell polyhedrons may become non-planar. Before any further design explorations can be made, the non-planar faces need to be planarised. Planarisation of non-planar faces can be formulated as an iterative projection method, which is a tried-and-tested methodology in computational geometry (1, 2 and 3).

Figure 1 shows an example application of the planarisation algorithm, implemented using the iterative projection method. At each time step, each face is projected onto either: the plane defined by its initial normal and the current centroid; a plane defined by a target normal; or a best-fit plane computed from its current vertex coordinates. Because faces are projected independently from one another, there will be multiple coordinates for a single vertex at the end of each time step. The average or the barycenter of the coordinates of a vertex is its new location for that time step. The procedure continues until a desired tolerance has been reached.

If there are no constraints enforced, each face projects itself to the plane defined by its current centroid and normal (Figure 1-b). In some 3D graphic statics applications, specified faces may need to stay fixed in their orientations. For example, some of the boundary faces of a multicell polyhedron corresponds to externally applied loads, which typically do not change in their magnitudes or locations during the design process. In addition, it may sometimes be desired to fix the orientations of certain members in the form diagram.

The orientation constraint can be enforced by updating the fix_normal attribute of the specified faces. Individual vertex constraints can also be set by updating the x_fix, y_fix, z_fix attributes for the specified vertices. Figure 1-c shows the planarisation, but this time with faces 2, 1 and 5 constrained to be perpendicular to the x, y and z axes, respectively. Figure 1. Planarisation of a polyhedral cell with non-planar faces: a) unconstrained planarisation, where the best-fit planes is used for each face at every iteration; and b) constrained planarisation, where some of the faces are given target normal vectors (faces 2, 1 and 5 are constrained to be perpendicular the x, y and z axes, respectively).

## Example

In this example, three random vertices are chosen to remain fixed during the planarisation. 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_rhino.selectors import VertexSelector

from compas.utilities import i_to_red

from compas_3gs.diagrams import ForceVolMesh

from compas_3gs.algorithms import volmesh_planarise

from compas_3gs.rhino import VolmeshConduit

from compas_3gs.utilities import compare_initial_current
from compas_3gs.utilities import volmesh_face_flatness

from compas_3gs.rhino import bake_cells_as_polysurfaces

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
# ------------------------------------------------------------------------------
layer = 'force_volmesh'

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

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

forcediagram.draw(layer=layer)

# ------------------------------------------------------------------------------
# 2. pick vertices to fix
# ------------------------------------------------------------------------------
vkeys = VertexSelector.select_vertices(forcediagram,
message='Select vertices to fix:')

# ------------------------------------------------------------------------------
# 3. planarise
# ------------------------------------------------------------------------------
forcediagram.clear()

initial_flatness = volmesh_face_flatness(forcediagram)

# conduit
conduit = VolmeshConduit(forcediagram)

def callback(forcediagram, k, args):
if k % 5:
current_flatness = volmesh_face_flatness(forcediagram)
face_colordict   = compare_initial_current(current_flatness,
initial_flatness,
color_scheme=i_to_red)
conduit.face_colordict = face_colordict
conduit.redraw()

# planarise
with conduit.enabled():
volmesh_planarise(forcediagram,
kmax=2000,
fix_vkeys=vkeys,
fix_boundary_normals=False,
tolerance_flat=0.01,
callback=callback,
print_result_info=True)

# update / redraw
# forcediagram.draw()

bake_cells_as_polysurfaces(forcediagram)