In this tutorial, we will model a joint in reference to one of a gridshell. Considerations of proper engineering are left out and the focus is solely on creating solids that are generated on aligned construction planes.

Operations on solids, like trimming or merging, are computationally intensive and will lock Grasshopper when parameters are changed upstream. Thus, the number of objects operated on should be limited. In this tutorial, this is done by creating a box that can be moved around and only the nodes inside the box are calculated.

## Grasshopper

The methods described in this tutorial expect that a surface for the gridshell is present and that there are segmented surfaces for each grid cell. To keep it simple, we will use a torus hereafter.

#### 0

##### Generate a gridshell surface and subdivide it

To generate a torus as base geometry, we create a CircleInputs Plane (P) Base plane of circle Radius (R) Radius of circle Outputs Circle (C) Resulting circle `30`

and then use End PointsInputs Curve (C) Curve to evaluate Outputs Start (S) Curve start point End (E) Curve end point Inputs Origin (O) Origin of plane Outputs Plane (P) World XZ plane `15`

and then use it as a section curve for Sweep1Inputs Rail (R) Rail curve Sections (S) Section curves Miter (M) Kink miter type (0=None, 1=Trim, 2=Rotate) Outputs Brep (S) Resulting Brep

To subdivide the surface of the torus, we first have to calculate the size of
the grid cells. We do this with DimensionsInputs Surface (S) Surface to measure Outputs U dimension (U) Approximate dimension in U direction V dimension (V) Approximate dimension in V direction Inputs A (A) Item to divide (dividend) B (B) Item to divide with (divisor) Outputs Result (R) The result of the Division `5`

to get corresponding values. The component Divide Domain²Inputs Domain (I) Base domain U Count (U) Number of segments in {u} direction V Count (V) Number of segments in {v} direction Outputs Segments (S) Individual segments Inputs Surface (S) Base surface Domain (D) Domain of subset Outputs Surface (S) Subset of base surface

#### 1

##### Create a box to capture nodes with

To place a box on the surface of the torus, we have to find the coordinates for
the box' base first. This is done with the component Evaluate SurfaceInputs Surface (S) Base surface Point (uv) {uv} coordinate to evaluate Outputs Point (P) Point at {uv} Normal (N) Normal at {uv} U direction (U) U direction at {uv} V direction (V) V direction at {uv} Frame (F) Frame at {uv} **S** is *reparameterized*. We
can now use a MD SliderInputs Base (B) Base plane X (X) Size of box in {x} direction. Y (Y) Size of box in {y} direction. Z (Z) Size of box in {z} direction. Outputs Box (B) Resulting box

#### 2

##### Isolate the captured nodes

To continue, we will further dismantle the grid cell surfaces with Deconstruct BrepInputs Brep (B) Base Brep Outputs Faces (F) Faces of Brep Edges (E) Edges of Brep Vertices (V) Vertices of Brep Inputs Brep (B) Brep for inclusion test Point (P) Point for inclusion test Strict (S) If true, then the inclusion is strict Outputs Inside (I) True if point is on the inside of the Brep. **V**. This
will give us a list of Boolean values that can be connected to a DispatchInputs List (L) List to filter Dispatch pattern (P) Dispatch pattern Outputs List A (A) Dispatch target for True values List B (B) Dispatch target for False values

We now have all the nodes but the surfaces, that are next to each other, share
the same vertices. Thus, by dismantling the surfaces, we got vertices that have
the same coordinates as vertices from an adjacent surface. Thus, we use Cull DuplicatesInputs Points (P) Points to operate on Tolerance (T) Proximity tolerance distance Outputs Points (P) Culled points Indices (I) Index map of culled points Valence (V) Number of input points represented by this output point *flatten* its input **P** to end up with a list of
unique nodes that will serve for modeling the joints.

#### 3

##### Select the surrounding surfaces

Next, we select the surfaces that connect to the identified nodes. The vertices
of each grid cell are separated in lists and this will help us to get the right
surfaces. We connect a Sort ListInputs Keys (K) List of sortable keys Values A (A) Optional list of values to sort synchronously Outputs Keys (K) Sorted keys Values A (A) Synchronous values in A Inputs Brep (B) Brep for inclusion test Point (P) Point for inclusion test Strict (S) If true, then the inclusion is strict Outputs Inside (I) True if point is on the inside of the Brep. Inputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'} *Reverse* input **L**. Now, if one of the vertices is within the box,
the value `1`

for *True* is forwarded. Using DispatchInputs List (L) List to filter Dispatch pattern (P) Dispatch pattern Outputs List A (A) Dispatch target for True values List B (B) Dispatch target for False values *flattened* will sort through the surfaces and select those which are
connected to our nodes.

#### 4

##### Select the connected curves

To find the curves which are connected to the nodes, we grab the edges **E**
from the dismantled grid cells and attach an End PointsInputs Curve (C) Curve to evaluate Outputs Start (S) Curve start point End (E) Curve end point *graft* both outputs. We now use Point In BrepInputs Brep (B) Brep for inclusion test Point (P) Point for inclusion test Strict (S) If true, then the inclusion is strict Outputs Inside (I) True if point is on the inside of the Brep. Inputs Keys (K) List of sortable keys Values A (A) Optional list of values to sort synchronously Outputs Keys (K) Sorted keys Values A (A) Synchronous values in A Inputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'} **L** *reversed*) we again forward `1`

for *True* to a DispatchInputs List (L) List to filter Dispatch pattern (P) Dispatch pattern Outputs List A (A) Dispatch target for True values List B (B) Dispatch target for False values *flattened*.

We now have all the edges that are completely or partially in the box, but
because adjacent cells have equal edges, there are some duplicate ones. We can
remove duplicate curves by getting
their midpoint as a Point On CurveInputs Points (P) Points to operate on Tolerance (T) Proximity tolerance distance Outputs Points (P) Culled points Indices (I) Index map of culled points Valence (V) Number of input points represented by this output point Inputs Points (P) Points to operate on Tolerance (T) Proximity tolerance distance Outputs Points (P) Culled points Indices (I) Index map of culled points Valence (V) Number of input points represented by this output point *Leave One*. A List ItemInputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'}

#### 5

##### Find proper vectors for construction planes

All cells of the gridshell have a different orientation in space and the construction planes of the joints should deviate as little as possible from the adjacent surfaces. For this, we calculate the mean vector by adding the normal vectors of the corresponding cells.

Before we can add the vectors, we have to find the cells that are associated
with a node. For this, we use AreaInputs Geometry (G) Brep, mesh or planar closed curve for area computation Outputs Area (A) Area of geometry Centroid (C) Area centroid of geometry Inputs Point (P) Point to search from Cloud (C) Cloud of points to search Count (N) Number of closest points to find Outputs Closest Point (P) Point in [C] closest to [P] CP Index (i) Index of closest point Distance (D) Distance between [P] and [C](i) **N** depends on the type of grid
(triangular, hexagonal etc.) and on the position of the cell within the grid. We
get this number from output **V** of the Cull DuplicatesInputs Points (P) Points to operate on Tolerance (T) Proximity tolerance distance Outputs Points (P) Culled points Indices (I) Index map of culled points Valence (V) Number of input points represented by this output point

We now have a separate list of associated surface indices for each node and a
List ItemInputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'} Inputs Surface (S) Base surface Point (uv) {uv} coordinate to evaluate Outputs Point (P) Point at {uv} Normal (N) Normal at {uv} U direction (U) U direction at {uv} V direction (V) V direction at {uv} Frame (F) Frame at {uv} *reparameterize* input **S**. A Panel`.5,.5`

will evaluate the surface at its center. At output
**N** we get the normal vectors and we add them with Mass AdditionInputs Input (I) Input values for mass addition. Outputs Result (R) Result of mass addition Partial Results (Pr) List of partial results Inputs Input (I) Input values for mass addition. Outputs Result (R) Result of mass addition Partial Results (Pr) List of partial results **U** to get the
mean orientation of each node.

#### 6

##### Create solids for joints

The next task is to create the construction planes
with the just created vectors: First we create planes perpendicular to the mean
normal vectors with Plane NormalInputs Origin (O) Origin of plane Z-Axis (Z) Z-Axis direction of plane Outputs Plane (P) Plane definition **O** we connect the
nodes from step 2 as origins and *flatten* input **Z** to which we connect the
normal vectors. Then, we append an Align PlaneInputs Plane (P) Plane to straighten Direction (D) Straightening guide direction Outputs Plane (P) Straightened plane Angle (A) Rotation angle **D** *flattened*.

Now that we have our construction planes, we will create a ring that the grid
members can later attach to. In a Panel*Multiline Data*
turned off, we write the inner and outer radius of our ring (here `0.10`

and
`0.12`

) and draw matching circles with CircleInputs Plane (P) Base plane of circle Radius (R) Radius of circle Outputs Circle (C) Resulting circle *graft*
input **R**). Explode TreeInputs Data (D) Data to explode Outputs Branch 0 (-) All data inside the branch at index: 0 Branch 1 (-) All data inside the branch at index: 1 Inputs Curve A (A) First curve Curve B (B) Second curve Outputs Surface (S) Ruled surface between A and B

The axes of the grid members will meet the ring at half height, so we have to
extrude it upwards and downwards. The direction of the extrusion vectors is
given by the mean normal vectors and we have to adjust their length. A panel
with `0.05`

will give us a value for half of the height and NegativeInputs Value (x) Input value Outputs Result (y) Output value Inputs Vector (V) Base vector Amplitude (A) Amplitude (length) value Outputs Vector (V) Resulting vector *graft* input **A** and **flatten** input
**V**. Now, each vector will be converted into two short vectors. But, we need
to use Flip MatrixInputs Data (D) Data matrix to flip Outputs Data (D) Flipped data matrix

To put it all together, we use an ExtrudeInputs Base (B) Profile curve or surface Direction (D) Extrusion direction Outputs Extrusion (E) Extrusion result *graft* and *simplify* input
**B** and *simplify* input **D** to get matching data streams. Because we have
two extrusions for each node, one upward and one downward, we can combine them
to a single Brep with Solid UnionInputs Breps (B) Breps to union Outputs Result (R) Union result

#### 7

##### Shorten length of grid members

We will continue with the curves from step 4. The goal is to shorten them, so
that they end on the center lines of the rings. In this example, we will cut off
`0.11`

from each end. After retrieving their initial LengthInputs Curve (C) Curve to measure Outputs Length (L) Curve length Inputs A (A) First operand for subtraction B (B) Second operand for subtraction Outputs Result (R) Result of subtraction `0.11`

. But we need this value for each line. Thus, we
have to use Longest ListInputs List (A) (A) List (A) to operate on List (B) (B) List (B) to operate on Outputs List (A) (A) Adjusted list (A) List (B) (B) Adjusted list (B) Inputs Data 1 (D1) Data stream 1 Data 2 (D2) Data stream 2 Outputs Result (R) Result of merge *graft* the inputs. We now have lists that contain two
values each.

To shatter the curves at these lengths, we have to calculate their curve parameters. For
this, we use Evaluate LengthInputs Curve (C) Curve to evaluate Length (L) Length factor for curve evaluation Normalized (N) If True, the Length factor is normalized (0.0 ~ 1.0) Outputs Point (P) Point at the specified length Tangent (T) Tangent vector at the specified length Parameter (t) Curve parameter at the specified length *graft* input **C**. We have to
set input **N** to *False*, which states that we are working with the actual and
not the normalized lengths. Next, we connect output **t** with a ShatterInputs Curve (C) Curve to trim Parameters (t) Parameters to split at Outputs Segments (S) Shattered remains *graft* the curves that we connect to input
**C**. The curves are now cut into three peaces. Because we only interested in
the middle part, we use List ItemInputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'} `1`

. We now
find the shortened curves at its output.

#### 8

##### Calculate vectors for construction planes

To create a section profile for each curve, we have to generate a construction plane. This should deviate as little as possible from the neighboring cells, so that the grid member has the same tilt to both adjacent surfaces (similar to step 5).

With Point On CurveInputs Point (P) Point to search from Cloud (C) Cloud of points to search Count (N) Number of closest points to find Outputs Closest Point (P) Point in [C] closest to [P] CP Index (i) Index of closest point Distance (D) Distance between [P] and [C](i) **N** to
look for is given by output **V** of Cull DuplicatesInputs Points (P) Points to operate on Tolerance (T) Proximity tolerance distance Outputs Points (P) Culled points Indices (I) Index map of culled points Valence (V) Number of input points represented by this output point Inputs Surface (S) Base surface Point (uv) {uv} coordinate to evaluate Outputs Point (P) Point at {uv} Normal (N) Normal at {uv} U direction (U) U direction at {uv} V direction (V) V direction at {uv} Frame (F) Frame at {uv} *reparameterize* its input **S**, set `.5,.5`

as **uv**-coordinate and calculate
the mean vector with Mass AdditionInputs Input (I) Input values for mass addition. Outputs Result (R) Result of mass addition Partial Results (Pr) List of partial results **N**.

#### 9

##### Create solids for grid members

Next, we will give body to the grid members. By using Perp FrameInputs Curve (C) Curve to evaluate Parameter (t) Parameter on curve domain to evaluate Outputs Frame (F) Perpendicular curve frame at {t} *reparameterizing* its input **C**, we can create planes perpendicular
to the curves. The location on the curves (input **t**) is set to their starting
point with `0`

. These planes are not yet aligned with our surfaces and we need
to use Align PlaneInputs Plane (P) Plane to straighten Direction (D) Straightening guide direction Outputs Plane (P) Straightened plane Angle (A) Rotation angle **P** has to be *grafted* so that the data structures match.

The actual section profile is drawn as a RectangleInputs Plane (P) Rectangle base plane X Size (X) Dimensions of rectangle in plane X direction. Y Size (Y) Dimensions of rectangle in plane Y direction. Radius (R) Rectangle corner fillet radius Outputs Rectangle (R) Rectangle Length (L) Length of rectangle curve **X** and **Y** dimensions `-0.045 To 0.045`

. This way, our curves remains
the axes of our members. We then use Sweep1Inputs Rail (R) Rail curve Sections (S) Section curves Miter (M) Kink miter type (0=None, 1=Trim, 2=Rotate) Outputs Brep (S) Resulting Brep *grafting* for matching
the data structures.

#### 10

##### Solve intersection for joints and grid members

In the steps before, we have created joints and grid members as solids, but they
still interpenetrate. We can now either trim the joints with the grid members or
the grid members with the joints by using the Solid DifferenceInputs Breps A (A) First Brep set Breps B (B) Second Brep set Outputs Result (R) Difference result

## But I only have a mesh…

Let’s have a look at what has to be changed, if we initially started with a mesh
instead of a surface. To quickly create a mesh, we use Mesh SurfaceInputs Surface (S) Surface geometry U Count (U) Number of quads in U direction V Count (V) Number of quads in V direction Overhang (H) Allow faces to overhang trims Equalize (Q) Equalize span length Outputs Mesh (M) UV Mesh Inputs Mesh (M) Mesh for face boundary extraction Outputs Boundaries (B) Boundary polylines for each mesh face

To rework step 1 (creating the capture box), we have to do a little more work to
find a replacement for Evaluate SurfaceInputs Surface (S) Base surface Point (uv) {uv} coordinate to evaluate Outputs Point (P) Point at {uv} Normal (N) Normal at {uv} U direction (U) U direction at {uv} V direction (V) V direction at {uv} Frame (F) Frame at {uv} Inputs X coordinate (X) {x} coordinate Y coordinate (Y) {y} coordinate Z coordinate (Z) {z} coordinate Outputs Point (Pt) Point coordinate Inputs Point (P) Point to search from Mesh (M) Mesh to search for closest point Outputs Point (P) Location on mesh closest to search point Index (I) Face index of closest point Parameter (P) Mesh parameter for closest point **I**. List ItemInputs List (L) Base list Index (i) Item index Wrap (W) Wrap index to list bounds Outputs Item (i) Item at {i'} Inputs Mesh (M) Mesh for face boundary extraction Outputs Boundaries (B) Boundary polylines for each mesh face

To create a construction plane for our box, we use Is PlanarInputs Surface (S) Surface to test for planarity Interior (I) Limit planarity test to the interior of trimmed surfaces Outputs Planar (F) Planarity flag of surface Plane (P) Surface plane

What remains, is to find a substitution for Deconstruct BrepInputs Brep (B) Base Brep Outputs Faces (F) Faces of Brep Edges (E) Edges of Brep Vertices (V) Vertices of Brep *Face Boundaries* into lines with ExplodeInputs Curve (C) Curve to explode Recursive (R) Recursive decomposition until all segments are atomic Outputs Segments (S) Exploded segments that make up the base curve Vertices (V) Vertices of the exploded segments **S**) to step 4. The ExplodeInputs Curve (C) Curve to explode Recursive (R) Recursive decomposition until all segments are atomic Outputs Segments (S) Exploded segments that make up the base curve Vertices (V) Vertices of the exploded segments **V**. The
start and end point of each face boundary are listed as two separate vertices,
but they are in fact the same point. Thus, we use Cull IndexInputs List (L) List to cull Indices (I) Culling indices Wrap (W) Wrap indices to list range Outputs List (L) Culled list

Now, every change is made to use the algorithm with meshes. It could be tweaked even more to use mesh faces for the calculation of the construction plane vectors. This optimization would be even more useful if additional Grasshopper Plugins would be used. For now, it’s fine for vanilla Grasshopper.