Form-finding 3
Vaults, minimal surfaces and mesh relaxation
Last updated
Vaults, minimal surfaces and mesh relaxation
Last updated
This segment investigates simulated generation of minimal surfaces [] such as thin elastic membranes. The approach emulates Frei Otto’s [] process of deriving some remarkable designs for lightweight tensile structures [] using physical models.
The general idea revolves around setting up a simple polygonal skeleton, performing subdivision to increase the mesh resolution and forcing the springs to shrink until the geometry reaches force equilibrium. The result resembles a heat-shrunk plastic sheet or an elastic fabric in tension.
In this example we manually defined a set of square surfaces, as seen in the figure below in black wireframe. We restrained its corners and after increasing the number of tiles per surface we fused them into one mesh. The simulation arrived at a shape which balances all tensile forces across the membrane.
In this example we require a list of surfaces and a resolution factor. We assume that the surfaces are somewhat well-connected with one another. They may for example be derived from a solid body. This assumption is important for ensuring that the mesh geometry does not tear itself apart while simulating. For each surface patch we produce a simple rectangular grid mesh using the “mesh surface” component. The resolution parameter is used for defining the number of grid tiles per direction.
Next we need to fuse all the mesh fragments into one coherent mesh. In the figure above one fragment is a 4 x 4 mesh grid plane (outlined in yellow), containing 25 points and 16 faces as seen below. We use the “mesh join” component to merge 18 mesh fragments into one mesh which now contains 18 x 25 = 250 vertices and 18 x 16 = 288 faces.
We need to remove duplicate mesh points i.e. points that are tiny bit spaced apart. This is typically caused by manual drawing inaccuracies. We use the “align vertices” component to achieve this. If we produce meshes via visual programming which is more accurate, we may skip this step.
Next we force the mesh normals to align themselves in a consistent manner as we move from face-to-face. Again this is needed because manually drawing surfaces is often done sloppily via copy & paste. We use the “unify mesh” component to order face normals. In the figure above we observe that this operation flipped the face order, i.e. from “[A, B, C, D]” to “[D, C, B, A]” of 32 faces!
Finally, we use mesh welding to make sure all faces share the same vertices in a connected manner. This ensures that we wont experience any mesh tearing. It also reduces the total number of mesh vertices which makes the simulation faster.
The only other point of interest in this setup is the way of expressing anchor points i.e. fixed particles, using points selected on screen rather than messy indexing seen earlier. To define immobile particles we will input a list of points selected from the viewport. If a mesh vertex is at the same position as an anchor point we will set the particle mass to zero thus fixing it in space.
We import multiple points from screen, stored in the “anchors” list and compute all distances between anchor and mesh vertex points using a Cartesian product. The table below, with “p” for particles and “a” for anchors captures the data generated.
…
…
…
…
…
…
…
…
…
If the distance is less than a tolerance value i.e. 0.01 modelling units, i.e. they are close enough to one another, then we can assume that a vertex point and anchor point do indeed overlap. The table below shows this result using some indicative true/false values as an example.
…
True
False
…
False
False
False
…
False
…
…
…
…
…
False
True
…
False
We can use the true/false boolean value by first converting it to a 1/0 integer and then sum the table’s entries per mesh vertex. The table below shows the result of the conversion and summation of rows.
…
1
0
…
0
0
0
…
0
…
…
…
…
…
0
1
…
0
1
1
…
0
The reason this works is because the intersection of a column and a row tells us if a vertex overlaps an anchor. Summing all those values per vertex tells us how many anchors overlap a vertex. The result is zero if the vertex is “free” or non-zero if the vertex in “fixed”. We can thus use this bit of information in an expression such as “Mass * ( 1 – Fixed )” using branchless inversion, or “if( Fixed = 0, Mass, 0 )” using the expression component syntax or “Mass if( Fixed == 0 ) else 0” using python syntax.
Practice
Experiment with producing different types of minimal surfaces either by manually drawing the initial setup of generating it by visual programming.
You may trivially upgrade the anchoring logic to support anchoring based on whether mesh points are on a plane or surface. This is because you can exchange the point-to-point distance with a point-to-other object distance component.
You may also constraint particles or surfaces, planes or curves using the closest point concept. For example you may relax a mesh on top of a sphere or a smooth surface such that triangulations flow nicely.