11 Jun, 2019

xGen Script Node – Simplified Venation

As architects we’re often searching out novel approaches to patterning for building facade design or custom building interiors. Algorithms that approximate processes in nature can be a good source material for pattern exploration. Their quick to run once developed and provide a large array of design variability with some simple tweaking.

Approximating the veining pattern, or venation, of a leaf can be done through a process of creating randomized points and then iteratively clustering neighbors. Below is a simplified approach to creating a venation pattern that can provide useful results.

In one of the more recent frequent updates to xGenerative Design the development team added the ability to create a Script Node. This allows the user to insert their own custom EKL code into an existing xGen graph. Kudos to the development team for providing this new node because it’s helpful is several different ways. For one, it provides a way to condense logic in a visual programming graph and avoid the Deutsch limit. It also introduces the ability to create loops. Much of visual programming is often about data mapping and so having the ability to also create arbitrary loops open the door to new possibilities.

xGen Setup

The first task is to create the base geometry which will provide the relevant inputs for the script node. We will need a starting point for the venation pattern allow with a series of sample points spaced semi-evenly around for which to form the pattern from. Initially the geometry can be modeled interactively then the graph interface can be utilized once the sample points need to be generated from the surface.

Script Node

When you first create a script node there’s just a single button to edit the script.

Editing the node takes you to a window where at the top you can add inputs and outputs. The section below that is a text editor to begin developing the custom script.

Once the inputs and outputs are established the node will display them, ready for connections to other nodes.

Establishing Inputs and Outputs

The node will have three inputs and one output. The inputs include: 1. startPt which is a GeometricPoint, 2. inputPts list, and 3. ptsDist to set the final distance between points in the final venation pattern. The single output, outputLines, is a list of values for the line segments that make up the final pattern.

Script Breakdown
  1. First create an empty list for the final venation points and add the start seed point.
let finalPts (List)
finalPts.Append(startPt.AsVector)
  1. Start looping through each input point to iteratively identify the closest neighbor within the final point list and move the input point adjacent to it.
for i = 1 while i <= inputPts.Size()
{
...
}
  1. First step in the loop is to convert or “cast” the current point as a Vector
pt2Vec = (inputPts[i]:GeometricPoint).AsVector
  1. Then find the point within the growing list of final points that is closest to the current input point. First calculate the Euclidean distance between the current input point and current final point in the loop. This can be done by first subtracting the vectors and then getting the square root of the summed squares. Then test to see if the distance calculated is greater than the value that shortestDist is currently set to. Once the loop is finished it will have chosen the closest point. To learn more about Vector methods take a look at the post here.
let shortestDist (Real)
shortestDist = 0.0
for j = 1 while j <= finalPts.Size()
{
set diffVec = pt2Vec - finalPts[j]
set vecDist = abs(sqrt((diffVec.Get(1,1)**2) + (diffVec.Get(2,1)**2) + (diffVec.Get(3,1)**2)))
if shortestDist == 0.0 or vecDist < shortestDist
{
pt1Vec = finalPts[j]
shortestDist = vecDist
finalDiffVec = diffVec
}
}
  1. Determine the move vector by reducing the final difference vector calculated previously. Apply the move vector to the current input point and add it to the final point list.
set moveVec = finalDiffVec * ((shortestDist-ptsDist)/shortestDist)
set pt2Vec = pt2Vec - moveVec
finalPts.Append(pt2Vec)
  1. The final step is the record the line segments that will make up the venation pattern. Extract the first, second and third values of each vector as a Length to get the final values used to draw the pattern’s line segments.
outputLines.Append(List((pt1Vec.Get(1,1):Length), (pt1Vec.Get(2,1):Length), (pt1Vec.Get(3,1):Length), (pt2Vec.Get(1,1):Length), (pt2Vec.Get(2,1):Length), (pt2Vec.Get(3,1):Length)))

Full Solution

Final Script Node
let i, j (Integer)

let finalPts (List)
finalPts.Append(startPt.AsVector)

for i = 2 while i <= inputPts.Size()
{
let pt1Vec, pt2Vec, diffVec, finalDiffVec, moveVec (Vector)
let vecDist (Real)

pt2Vec = (inputPts[i]:GeometricPoint).AsVector

let shortestDist (Real)
shortestDist = 0.0
for j = 1 while j <= finalPts.Size()
{
set diffVec = pt2Vec - finalPts[j]
set vecDist = abs(sqrt((diffVec.Get(1,1)**2) + (diffVec.Get(2,1)**2) + (diffVec.Get(3,1)**2)))
if shortestDist == 0.0 or vecDist < shortestDist
{
pt1Vec = finalPts[j]
shortestDist = vecDist
finalDiffVec = diffVec
}
}

set moveVec = finalDiffVec * ((shortestDist-ptsDist)/shortestDist)
set pt2Vec = pt2Vec - moveVec
finalPts.Append(pt2Vec)
outputLines.Append(List((pt1Vec.Get(1,1):Length), (pt1Vec.Get(2,1):Length), (pt1Vec.Get(3,1):Length), (pt2Vec.Get(1,1):Length), (pt2Vec.Get(2,1):Length), (pt2Vec.Get(3,1):Length)))
}
Final xGen Graph

Source File

Here is the 3dxml file for the above example.

Tags: ,

About : Nuri Miller

Nuri Miller is CO Architects’ Director of Digital Design and Technology, where he plays a key role in the firm’s strategic use of technology to support and enhance the design process. He has applied his technical skills and knowledge to international projects for Walt Disney Imagineering and Gehry Partners.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.