10.10 Surface Processing
A careful observation of the normals function shows that it iterates along a surface’s quadrilaterals, computing the normal to each one. Naturally, we can generalize this process so that any operation can be applied to a surface’s quadrilaterals. The transition of the normals function to a higher-order function is done by replacing the function that calculates the normal with an arbitrary function:
iterate_quadrilaterals(f, ptss) =
map((pts0, pts1) -> map((p0, p1, p2, p3) -> f(p0, p1, p2, p3), pts0, pts1, pts1[2:end], pts0[2:end]),
ptss,
ptss[2:end])
The computation of the normals to a surface can now be treated as a mere particularization of the iterate_quadrilaterals function:
normals(ptss) = iterate_quadrilaterals(normal, ptss)
The great advantage, however, lies in the fact that we can now create many other ways of processing a surface. As an example, let us consider the creation of a net, as the one shown in this figure. The shape of the net is defined by the following parametric surface:
\[\left\{ \begin{aligned} x(u,v)& = u\\ y(u,v)& = v\\ z(u,v)& = \frac{7}{100}(\sin(u)+\sin(2(v-2))) \end{aligned}\right.\] \[0\leq u \leq 5\] \[0\leq v \leq 5\]
A net made of crossed cylinders.
The creation of the previous net can be done by creating a cross on each quadrilateral of the surface. The cross can be modeled by using two cylinders whose ends coincide with the vertices of the diagonals of the quadrilateral. For the cylinders to adapt to different quadrilaterals, we can consider that the cylinders’ radius is proportional to the size of the quadrilateral. The following function implements this approach:
quadrilateral_cross_cylinders(p0, p1, p2, p3) =
let r = min(distance(p0, p1), distance(p0, p3))/10
cylinder(p0, r, p2)
cylinder(p1, r, p3)
end
To create the net of this figure we just need to evaluate the following expression:
iterate_quadrilaterals(quadrilateral_cross_cylinders,
map_division((u, v) -> xyz(u, v, 0.07*(sin(u)+sin(2*(v-2)))),
0,
5,
20,
0,
5,
20))
The function iterate_quadrilaterals can be used either for different surfaces or for different functions to iterate over the quadrilaterals of those surfaces. This figure shows different constructions performed over the surface defined by the following parametric functions:
\[\left\{ \begin{aligned} x(u,v)& = u\\ y(u,v)& = v\\ z(u,v)& = \frac{4}{10}(\sin(u+v)+e^{\frac{\left|u-1\right|}{10}}+\sin(v-1)) \end{aligned}\right.\] \[0\leq u \leq 3\] \[0\leq v \leq 6\]
Different constructions made from the iteration of an element over a surface’s quadrilaterals. From left to right and top to bottom, the repeating element is: (1) four cylinders connected by a sphere; (2) a cube; (3) a cone; (4) a spherical cap; (5) a cylinder; and (6) a parallelepiped of random height.
This figure shows the result of iterating the very same functions over another surface defined by:
\[\left\{ \begin{aligned} x(u,v)& = u\\ y(u,v)& = v\\ z(u,v)& = \frac{4}{10}\sin(u\cdot v) \end{aligned}\right.\] \[-\pi\leq u \leq \pi\] \[-\pi\leq v \leq \pi\]
Different constructions made from the iteration of an element over a surface’s quadrilaterals. The repeating elements are the same as in the previous figure but applied on a different surface.
10.10.1 Exercises 56
10.10.1.1 Question 203
For each of the examples shown in this figure (and, equivalently, in this figure), define the function that receives four points as arguments and that, when iterated over the surface coordinates using the iterate_quadrilaterals function, reproduces the model.
10.10.1.2 Question 204
Define a function that, given the coordinates of the four vertices of a quadrilateral, creates two interlaced cylindrical tubes along the diagonals of the quadrilateral, as presented in the following image:
Use the created function as an argument of the iterate_quadrilaterals function to generate the fabric that is presented below:
10.10.1.3 Question 205
The approach used in the previous exercise is not adequate when the surface in question is not planar (or near planar), because the cylindrical tubes of two adjacent quadrilaterals may not be properly aligned. In that case, overlapping or, even worse, empty spaces will occur.
Redefine the creation process of the fabric using interlaced cylindrical tubes, but where each cylindrical tube corresponds to a complete wire (and not just the section contained in a quadrilateral). To simplify, we can assume that the wires develop along the lines and columns of the coordinate matrix of the surface, to generate images as the following:
In several previous examples there was a separation between the generation of a surface’s coordinates (using, for example, the map_division function) and the use of these coordinates, for example, for visualization (using the surface_grid function) or for processing (using the iterate_quadrilaterals function).
This separation is advantageous because it allows us to arbitrarily combine different functions that generate coordinates with different functions that use those coordinates. Another interesting possibility is the use of functions that process the coordinates to produce new coordinates. A simple example would be the application of a scale effect over a surface, by multiplying the scale by each of the coordinates, thereby generating a new set of coordinates.
A slightly less simple but far more interesting example is the creation of trusses whose shape follows a surface. As we have seen in section Trusses, trusses are composed of nodes joined by bars to form for instance quadrilateral pyramids. To generate a truss that follows a surface we can simply build the trusses pyramids with their base set on the surface and their top located on the normal vector to that surface. The function space_truss (that we developed in section Space Trusses) can build these trusses, as long as it receives the positions of the trusses’ nodes in the form of an array. This array must contain an odd number of arrays of positions, according to the sequence: base nodes, top nodes, base nodes, top nodes, ..., base nodes.
With these assumptions in mind, to build a truss that follows a surface we can start by generating the coordinates of that surface, but conveniently spaced to serve as positions to the base nodes of each truss pyramid. Then, for each quadrilateral of coordinates, we have to find the pyramid’s top node. For this, we can assume that the trusses will use, whenever possible, bars with the same length \(l\). This implies that the top node of each pyramid is located on the normal to the quadrilateral defined by the base nodes, at a distance \(h=\frac{l}{\sqrt{2}}\) from the center of that quadrilateral. Therefore, we have to calculate both the quadrilateral’s center and normal, as illustrated with the following function:
pyramid_top(p0, p1, p2, p3) =
let h = (distance(p0, p1)+distance(p1, p2)+distance(p2, p3)+distance(p3, p0))/4.0/sqrt(2)
quadrilateral_center(p0, p1, p2, p3)+quadrilateral_normal(p0, p1, p2, p3)*h
end
The next step will be to use this function to compute the positions of the pyramid’s top nodes along the surface mesh coordinates. This will create new sequences of coordinates that are inserted between the sequences of coordinates of the original mesh, in order to reproduce the alternation between base nodes and top nodes of the truss.
The two following functions perform this task:
insert_pyramid_top(ptss) =
length(ptss) == 1 ?
ptss :
[ptss[1],
insert_pyramid_top_row(ptss[1], ptss[2]),
insert_pyramid_top(ptss[2:end])...]
insert_pyramid_top_row(pts0, pts1) =
[pyramid_top(pts0[1], pts1[1], pts1[2], pts0[2]),
length(pts0) == 2 ? [] : insert_pyramid_top_row(pts0[2:end], pts1[2:end])...]
Finally, given any mesh, we simply have to combine the creation of the pyramid’s top nodes with the creation of the truss, i.e.:
space_truss(insert_pyramid_top(mesh))
This figure shows the result of creating a truss over the same surface used in this figure.
Truss built over a surface.
Naturally, we can combine the truss construction with any other surface. This figure shows a truss created over the same surface used in this figure.
Truss built over a surface.
Finally, out of curiosity, this figure shows a truss built over a Möbius strip. This truss was produced by evaluating the following expression:
space_truss(insert_pyramid_top(mobius_strip(0, 4*pi, 160, 0, 0.3, 5)))
A truss with the shape of a Möbius strip.
10.10.2 Exercises 57
10.10.2.1 Question 206
If you look closely to the truss with the shape of a Möbius strip you will notice an error. Identify it and explain it.
10.10.2.2 Question 207
Consider the barrel cacti shown in the following image:
Define a function that, conveniently parametrized, can create barrel cacti as the previous ones.