Organic elevation design from image file using dynamo and Revit

In the previous blog post, we touched upon an interesting idea by creating a parametric facade generator that had the ability to analyse an Image file and then convert it’s pixel data into a usable form. which would later be used to determine the overall look of an architectural facade. in that project we used a non-destructive approach. this means that once the code is written, the user can run the program indefinitely, as many times as they wish with a number of different image files to achieve different results. this is useful when slow and gradual fine tuning of the original design is desirable. if you haven’t read that blog post yet, I highly recommend checking it out by clicking here.

But today, we’re going to be working on a whole different project. in this blog post, I’ll walk you through the process of creating an automated Dynamo Graph that would generate a parametric facade element from a given image file. in other words the designer would “model” this parametric three dimensional form in a two dimensional photo editing environment such as Adobe Photoshop or a similar application, and then use this workflow to transform the flat image into 3d. and finally import the geometry right into Revit for visualization and further documentation. The process consists of four separate steps, and since we’re going to generate our geometry from an image file, naturally the first step is to select, analyze and prepare our bitmap related information. then we can use our newly acquired pixel data to generate an array of points that will later be used to create the final form. so with all that out of the way, lets get coding already!

Although the same results can be achieved with traditional modeling techniques, but the main advantage of using Dynamo is the fact that, once the code is written, generating different results is super fast and extremely efficient.

Step 1: data extraction

This stage includes defining a fixed number of samples that we’re going to extract from our bitmap, it ensures that the code can run smoothly on high definition image files since it eliminates the need to loop and iterate through each and every pixel. here we also implement a mechanism that allows our code to programmatically detect the aspect ratio of the image file and assign a suitable number of samples in both X and Y directions.

High definition image available here
  1. Select image file path.
  2. Read from file and display a preview (optional).
  3. Acquire image width and height using Image.Dimensions.
  4. Input node for the max amount of samples. (100 in this case).
  5. boolean code block (W >= H) reports whether the image is portrait or landscape.
  6. Python script: assign smaller number of samples to the shorter side of the image file.
  1. If” code block: assign max samples to the longer side of the image file.
  2. Image.Pixel: extract the pixels based on the given number of samples.
  3. we want pixel columns as opposed to rows therefore we transpose the list.
  4. using Color.Brightness to get the brightness value of each pixel.

here is the python script used in the graph above (we could’ve just used another If node and just alternate the “true” and “false” input slots).

# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

check = IN[0]
#simple if statement 
if check == True:
    OUT = IN[1]
else: OUT = IN[2]

Step 2: generating array of points

In this step we’ll generate an array of geometrical dynamo points using Point.ByCoordinates node, but to do that we’ll need X,Y and Z coordinates of each point (we’ll acquire those from the pixel data we generated in the previous step with the help of a few Python nodes). lastly we’ll use an additional piece of Python code to add two more points to each sub-list of points. one in the beginning and one in the end of each column to close the loop (note that these additional points are always located of the XY plane, in other words they always have a Z coordinate of zero).

  1. Three Python code snippets, one for generating each coordinate for it’s respective point.
  2. mapping the z values to a user defined range ( this will allow the user to control the height of the geometry).
  3. using Point.ByCoordinate to generate the list of points.
  4. additional Python Code to add the remaining points needed to for a closed loop.

Here is all the Python code that was used:

#Pythone code X
# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

zdepth = IN[0]
filteredX = [[] for i in range(len(zdepth))]
columnNumber = 0


for column in zdepth:
	for z in column:		
		filteredX[columnNumber].append(columnNumber)
	columnNumber += 1

OUT = filteredX
#Python code Y
# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

zdepth = IN[0]
filteredY = [[] for i in range(len(zdepth))]
columnNumber = 0


for column in zdepth:
	zIndex = 0
	for z in column:		
		filteredY[columnNumber].append(zIndex)
		zIndex += 1
	columnNumber += 1

OUT = filteredY
#Python code Z
# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

zdepth = IN[0]
filteredZ = [[] for i in range(len(zdepth))]
columnNumber = 0


for column in zdepth:	
	for z in column:				
		filteredZ[columnNumber].append(z)	
	columnNumber += 1

OUT = filteredZ
# Python code Additional Points
# Enable Python support and load DesignScript library
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

lists = IN[0]

for pts in lists:
	if pts[0].Z != 0:
		pointS = Point.ByCoordinates(pts[0].X,pts[0].Y,0)
		pts.insert(0,pointS)
	if pts[-1].Z != 0:
		pointE = Point.ByCoordinates(pts[-1].X,pts[-1].Y,0)
		pts.insert(len(pts),pointE)
OUT = lists

Step 3: generating solid geometry

Now that we have the much needed list of points, the rest is just a matter of generating the relevant kinds of geometry, this is a rather painless process thanks to the amazing geometry generation tools offered by Dynamo.

  1. Generating curves that run trough each of our point sub-lists.
  2. joining the curves into PolyCurves.
  3. closing the curve loop using PolyCurve.CloseWithLine.
  4. Generating surfaces from the curve loops.
  5. turning the surfaces into solid Geometry using Surface.Thicken.

Step 4: exporting to Revit

Now all that’s left is to export the geometry back to Revit. there are many approaches to do so, in a real world project you might wanna import the geometry in the form of a family, but in this demonstration I’ll use a simple ImportInstance.ByGeometries node for the sake of simplicity.

  1. Removing potential problematic instances using Manage.RemoveNulls, this is a protective measure against importing errors down the line.
  2. Importing the geometry into Revit using ImportInstance.ByGeometries.

And… we’re done! bellow are some examples designed using this very graph that we just wrote together, this was a rather long blog post but I personally had a ton of fun creating it. thank you so much for your time and I really hope that this tutorial was of some value to you. take care and keep creating!