Scene optimizer for 3ds Max

Scene optimizer is a simple and easy to use maxscript plugin that utilizes the ProOptimizer feature of 3ds Max to reduce the geometrical complexity of selected items based on their distance from a specified camera (or any other object for that matter). Scene optimizer can be used in a number of scenarios, but it’s particularly useful in optimizing trees and other scene elements in an architectural visualization project.

Download Scene optimizer:

Click here to download Scene optimizer


How to use:


At times its rather difficult to determine the distance of objects in 3ds Max, for that you can use our Distance finder script:

Click here to download Distance finder

Installing and using maxscript macros is extremely simple and straight forward (just as easy as dragging and dropping the maxscript into 3ds max). I hope that this blog post will be of some help to you, enjoy the rest of your day 😉 .

Scale Calculator for architectural drawings

Hello and welcome to this blog post. As an architect it’s always been a challenge to quickly and easily determine the scale of a CAD drawing or to change it to a new one. thus I have programmed a little application called Scale Calculator which addresses that very problem. It’s not extremely polished but I’ve decided to share it with you guys here, hoping that it will help you in your day to day life working with technical drawings of various scales.

Edit: Scale calculator is now a web application, start running it right in your browser. click here.

Click here to download the application.

How to use the application:

Scale Calculator is extremely easy and self explanatory to use, all you really need to do is to download the installer from the link above and go through the simple installation process. once that’s done, a new icon should appear on your desktop with the name Scale Calculator. Once you open that up, you’re greeted with a super simple user interface. The application has two major functionalities:

  • Scale Finder: this functionality asks for a dimension from the drawing and it’s real-world value to find the scale of the drawing (units are irrelevant as long as the same unit is used for both inputs, e.g. in a 1:100 drawing, a 1m wide door measures exactly 1cm. So the user needs to input something like this: drawing dimension = 0.01 and real dimension = 1).
  • Scale Converter: this functionality asks the user to input the current and the desired scale in n:n format (1:100 or 1:25) and it calculates the factor by which the drawing should be enlarged or reduced (simply multiply the drawing by the given factor inside of AutoCad or a similar drafting application).

It’s true that most of the time we tend to use a small number of scales in our day to day lives and it’s very easy to convert them on the fly, but every now and then we might come across an irregular scale factor, this is when this tool might come in handy. Anyways I hope this post delivers some value to you and have an amazing day!

Progress reporting plugin for Revit

hello and welcome to this blog post! today we’re going to explore some of the tools that are exposed to us through the Revit API (Application programming interface), in order to create a simple yet effective progress tracking addin that can help the team leaders and project managers to better supervise the overall design and documentation process. the main difference between using the API and Dynamo is that Dynamo has a set of predefined nodes that each perform a certain task once you run the dynamo graph. but as intuitive as that is, it also quite obviously creates a number of serious limitation for us as developers such as:

  • Dynamo cannot run in the background as the user is working.
  • A Dynamo graph is pretty much open to the public so anyone can read through it, thus it’s not an easy task to protect your code. (although ZeroTouch partially resolves this issue).
  • An optimized addin can run much faster than a dynamo graph (specially for more complex tasks across a larger project), not to mention that the user does not have to launch Dynamo at all.
  • you have the ability to work in a fully supported IDE with intellisense and other functionalities.
  • The ability to use either WinForms or WPF to design an easy to understand user interface.
  • Last but not least, it’s extremely easy to incorporate third party libraries and DLLs into our code.

so now that you’re familiar with the main benefits of developing a Revit addin, let us get started!


Before we start writing any code though, you need to prepare a suitable programming environment. that includes installing and setting up your IDE, as well as making sure that the code can execute once it has been written. If you’re not familiar with Visual Studio, C# and/or writing addins for Revit, I strongly recommend that you read and follow through this beginner’s tutorial on Autodesk Knowledge Network: My first Revit plugin, you’ll also need a solid understanding of C# and general programming concepts. this blog post is more or less an overview of the whole process and is not going to go through each and every detail.

The main entry point of our program is going to be a class with an IExternalApplication interface. Here we can tell Revit that We’re going to be creating an addin and that we want our addin to have it’s own panel inside of Revit. Then we’ll proceed with adding buttons to the predefined panel. Each button is then connected to a class that has an IExternalCommand interface. This is where the majority of our code lives, since it is considered a “valid API context” for us to make our API calls from. A “valid Revit API context” is a concept that you’ll be hearing about quite often if you’re into creating addins for Revit. What it means in simple terms is that: you cannot make API calls where ever you want in your code, rather Revit is very specific in how your addin can communicate with it. Usually the builtin methods of IExternalApplication and IExternalCommand are considered “valid API context”. now with that out of the way, let’s add our very own panel and buttons to Revit. For the sake of simplicity we’ll call our buttons ON and OFF. they will enable and disable the progress tracker respectively.

class App : IExternalApplication
    {       
        public Result OnStartup(UIControlledApplication a)
        {  
           //Creating the pannel  
           string tabName = "Automation";
           a.CreateRibbonTab(tabName);
           RibbonPanel panel = a.CreateRibbonPanel(tabName, "Custom Tools");
           PushButtonData SJO_BtnData = new PushButtonData("SJO_BtnData", "SJO", Assembly.GetExecutingAssembly().Location, "modlessForm1.Command")
           //Creating and adding the buttons
           //adding button for updater on
           PushButtonData UpdateOn = new PushButtonData("UpdateOn", "ON", Assembly.GetExecutingAssembly().Location, "modlessForm1.Command6")
            {
                ToolTip = "This turns the updater on",
            };
           PushButton UpdateOn_Btn = panel.AddItem(UpdateOn) as PushButton;
           //adding button for updater off
           PushButtonData UpdateOff = new PushButtonData("UpdateOff", "OFF", Assembly.GetExecutingAssembly().Location, "modlessForm1.Command7")
            {
                ToolTip = "This turns the updater off",
            };
           PushButton UpdateOff_Btn = panel.AddItem(UpdateOff) as PushButton;   
           return Result.Succeeded;
        }
    }
Our (ON and OFF) buttons have been successfully added to Revit.

The next step is to program each button. This is done through two IExternalCommand, one for each button. In the ON button command, we’ll have to trigger a windows form in order to collect relevant input data from the user. then we’ll proceed to subscribing to two very useful Revit events (OnDocumentChanged event and Idling event). The first event raises every time the user modifies the document in any way. The second event raises whenever Revit is ready to receive API calls, hence the name Idling event. we’ll be using both of these events in conjunction to run our command every time the user performs any changes to the document, but still we want to make sure that the code runs only when Revit is “Ready” to to receive calls. In other words when Revit is in Idling mode. This allows for our code to run alongside the normal tasks of Revit without any conflicts.

First we’ll be using FilteredElementCollector to collect all the views that exist in the project. Then we’ll store their names in variables so that we can populate our UI later on. Finally, we’ll call our form to show up.

UIApplication UiApp = commandData.Application;
            Application App = UiApp.Application;
            UIDocument UiDoc = UiApp.ActiveUIDocument;
            Document doc = UiDoc.Document;

            //assigning to global variable for the document
            GlobalVariables6.m_document = doc;
            GlobalVariables6.m_path = string.Empty;

            //select view
            FilteredElementCollector collector = new FilteredElementCollector(doc);
            List<Element> views = collector.OfCategory(BuiltInCategory.OST_Views).WhereElementIsNotElementType().ToList();

            //string viewName = "Progress";

            List<string> viewnames = new List<string>();
            foreach (Element item in views)
            {
                viewnames.Add(item.Name);
            }
            GlobalVariables6.m_viewNames = viewnames;

            //show the modal form
            using (Form3 viewForm = new Form3(this))
            {
                viewForm.ShowDialog();
            }

            foreach (Element item in views)
            {
                if (item.Name == GlobalVariables6.m_viewName)
                {
                    view = item;
                }
            }

            //subscribe to event if the conditions are met
            if (GlobalVariables6.m_run == true)
            {
                App.DocumentChanged += OnDocumentChanged;
                UiApp.Idling += OnIdlling;
            }

            return Result.Succeeded;

Since the form is modal, the code will stop executing right after the form opens and awaits user input. The form looks something like this:

Designing the user interface of our addin

Right after the user inputs the required information and presses OK, we’ll subscribe to the above mentioned events. keep in mind that subscribing to events must be handled with care. You should always unsubscribe from that event once you’re done using it. this is the rest of the command that will get executed post user input which includes both of the event handlers:

 public static void OnIdlling(object sender, IdlingEventArgs e)
        {
            if (GlobalVariables6.m_docChanged == true)
            {
                string path = GlobalVariables6.m_path + "/frame-" + frameNumber.ToString() + ".jpg";
                var opt = new ImageExportOptions
                {
                    Zoom = GlobalVariables6.m_zoom,
                    ZoomType = ZoomFitType.Zoom,
                    FilePath = path,
                    FitDirection = FitDirectionType.Horizontal,
                    HLRandWFViewsFileType = ImageFileType.JPEGLossless,
                    ImageResolution = ImageResolution.DPI_600,
                    ExportRange = ExportRange.SetOfViews,
                };
                IList<ElementId> viewId = new List<ElementId>();
                viewId.Add(view.Id);
                opt.SetViewsAndSheets(viewId);
                GlobalVariables6.m_document.ExportImage(opt);            
                frameNumber++;
                GlobalVariables6.m_docChanged = false;
            }
        }

        static int frameNumber = 1;

        public static void OnDocumentChanged(object sender, DocumentChangedEventArgs e)
        {
            GlobalVariables6.m_docChanged = true;
        }

The heart of our code is the OnIdling event handler. This is where all of the magic happens. a set of rules have been describe in this method that are responsible for the image import process. the process looks something like this:

  1. OnDocumentChanged event gets triggered by the user, which will in return assign a true value to the m_docChanged variable (this is a public static variable within a class called GlobalVariables6).
  2. Once the user is done modifying the document, Revit raises the Idling event which signals to our program that it’s safe to run.
  3. Our program checks to see whether or not the user has indeed made any changes using a conditional statement that checks the value of the m_docChanged variable.
  4. If it’s true, the program collects user inputs and uses them accordingly (save directory, view name and zoom level)
  5. We use document.ExportImage to export the images based on the predefined rules and condition.
  6. finally we increment the frameNumber integer that is used to name the frames.

Turning this off is very simple. We simply unsubscribe from the event and we’re done:

class Command7 : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {           
            commandData.Application.Application.DocumentChanged -= Command6.OnDocumentChanged;
            commandData.Application.Idling -= Command6.OnIdlling;
            GlobalVariables6.m_run = false;
            return Result.Succeeded;
        }
    }

The plugin is now complete!

Every time the user makes any changes to the model, this plugin exports a preview of the image to the designated directory.

Here is the result of our work:

Animating almost anything in Revit using Dynamo.

As amazing as Autodesk Revit is for modeling and documenting a building, there is no question of the fact that it’s not exactly the strongest when it comes to animation. in this tutorial, we’ll go over a few techniques within Dynamo that will allow us to animate “almost” anything in Revit. now I should also mention the fact that not everything is programmatically adjustable in Revit but the general convention is that if you can edit something in the user interface, then you’ll probably be able to animate it (of course there might be exceptions to this; hence the word “almost” in the title of this blog post).

If all you wanna do is to create a simple walk-through by animating the camera and it’s target, then it would be foolish not to use the builtin walk-through creation tools offered by Revit. but if you’re after something more specific with a greater level of control of the parameters of every family instance, then this tutorial is for you. I should also point out that the methodology described here is very much inspired by a custom Dynamo package called Dynanimator which is developed by BadMonkeysTeam. although you do not need to install the package in order to be able to follow along with this tutorial, keep in mind that the general approach that’s explained here is borrowed from Dynanimator and is modified to suit our needs. feel free to download and install this excellent package and experiment with it. now without further ado, let us get started.


Steps:

  • creation of the animation_function node.
  • Creation of the animation node.
  • assembling the graph and feeding in the required information.

Step 1: “animation_function” node

this is the heart of the code, this is where we define our rules and every time that we wish to re-purpose our graph to animate something else, we’ll do it here. the big idea is that, we’ll lay the ground work of the changes that we’ll be making to the model here with a simple twist, we want the changes to happen one after the other for every single iteration (frame). so the general order of execution is as follows:

  • read which iteration we’re at.
  • read the input information.
  • execute change number 1.
  • execute change number 2.
  • execute change number 3, 4 etc….
  • and finally execute the last command: exporting the frame to a user-defined directory with proper naming.

the changes can be anything, this is exactly why this method is so useful, we have so much freedom over what is required to be animated and how. please keep in mind that the changes should be governed by the “iteration” value since we’re trying to generate the frames of an animation. for the purposes of this demonstration, we’ll be animating “object rotation” but the same principles with minor tweaks and changes can be applied to animate other aspect of the model, here is the graph for animation_function.dyf:

animation_function.dyf custom node
  • Input nodes: element, iterations, step, angle, view, path.
  • Change #1: rotating the instance based on this particular time-step. we determine the angle of each iteration based on a few factors (total number of iterations, current step and user defined angle). this is the formula: 1 / iteration * step * angle.
  • Final command: exporting the frame: here we use String.Concat to assemble a string variable using some of our input nodes, notice that the “step” input node is used to make the names varied and thus prevent overwriting of files during the export process. this also makes it easy to find particular frames later on.
  • We plug the last node of every action into a Transaction.End node to close their transactions. we finally feed those into a Function Compose node.

Step 2: “animation” node

there is really nothing special about this node, all that it ever does, is prepare the input information and iteratively run a given function (animation function node) for a total of n times. with n being the user-defined number of iteration or in other words the total number of frames in the whole animation. here is the graph for the custom node animation.dyf:

animation.dyf custom node
  • Input nodes: iteration, angle, element, path, view.
  • Here we place our animation_function custom node that we just created. notice how every slot has a corresponding input assigned to it except for “step”. leaving this argument unassigned turns our node into a function which can then be applied to a range of values using a List.Map node.
  • Outputting the results using an Output node.

Congratulations! our custom Dynamo node is now finished! all that is left to do is to feed in the required input arguments and click enter. a series of .png files will be exported to our defined directory which can then be processed into a video file quite easily:

using the custom node “animation” that we just created.
Rotation animation (demonstrated in this post)
animating family parameters

Kinetic Facade design, with the ability to responds to solar exposure.

with the latest technological advancements in the field of smart building and kinetic architecture, today we’re able to design and document and erect buildings that are not only aware of their surrounding climactic and geographical context, but are in fact able to analyze and intelligently respond to those factors by a variety of ways, including physical modification of their individual architectural components for higher levels of optimization.

one such example of a kinetic building is the Kiefer Technic showroom located in Bad Gleichenberg, Austria. apart from the aesthetically interesting and visually pleasing collective form of the external parametric shading devices, the building’s smart facade also play a tremendous role in regulating the amount of solar radiation that the building is exposed to throughout the day, and all year long. this kind of design allows for higher flexibility during the building’s design phase, as well as providing well studied and deeply optimized solutions to the natural lighting and thermal comfort design problems. another extremely well known example of kinetic architecture is Al-Bahar Towers in Abu dhabi, UAE. in this blog post we’re going to explore the idea of kinetic architectural elements inside of Revit by creating a solar responsive facade that takes Geo-location, time and date information from our Revit model and adjusts itself in accordance to these parameters. so without further ado, let’s get started.

Kiefer Technic Showroom located in Bad Gleichenberg, Austria. it’s dynamic facade can be changed and altered in accordance with the external weather conditions, as well as individual user preference.

Required steps:

  1. Modeling the kinetic facade units as an adaptive component inside of Revit.
  2. Applying the facade elements to the building mass using Dynamo and Lunchbox.
  3. Collecting Geo-Location , date and time data from Revit and importing it into Dynamo.
  4. Using the ladybug package to calculate the sun path and sun’s angle of incidence on our building.
  5. Extracting orientation data from each instance of our panels family.
  6. Adjusting the adaptive component’s “openness” value based on it’s orientation and it’s angle with the sun.
ordinary adaptive components not corresponding to the sun…
Result: Smart panels that adjust according to sun’s position…

Step 1: modeling the adaptive component

We’ll start by creating the adaptive component family. the reason for choosing an adaptive family as opposed to a curtain panel family is that generally, adaptive families allow for more advanced and complex geometry, since they’re not dependent on a planar curtain wall to be deployed, the user can deploy them however they want with four clicks (once for every adaptive point). but in our case we’ll use Dynamo to programmatically place our components so it’s even easier. creating families in Revit is a rather wide topic on it’s own and well outside of the scope of this tutorial, so the reader is expected to have a basic knowledge of how adaptive families are created and how they’re controlled parametrically . with that being said, let’s get started. once we’re in the adaptive component modeling environment, proceed with creating the skeleton using a collection of points and reference lines as demonstrated in the figure bellow.

skeleton model for the adaptive component.

Now is a good time to introduce parameters, and the way they’re involved in the behavior of this family:

the parameters which drive this adaptive component and their formulae.

So we have about 7 vital parameters ( apart from aesthetic ones such as thickness and glass material). now we’ll understand each parameter’s purpose as well as their formula.

  1. Direction line Length: this line exists for data inquiry purposes only (we’ll get the panels’ orientation data from this). the length is irrelevant we’re only interested in the line’s direction, thus a value of 10mm is sufficient in our case.
  2. Offset: this parameter controls the height of the joint. it’s directly related to how “opened” the panel is. it’s calculated using the following formula: (length of the panel / 4) * openedRaw.
  3. Report (Length of the panel): this is a reporting parameter, meaning that it does not directly affect the family. rather, it provides us with important dimension information that will be used to calculate the other parameters. this particular reporting parameter provides the total length of the panels.
  4. Fold Parameter: this controls the position of the folding edge along the reference lines. it has the following formula: 0.5 * (1 – openedRaw)
  5. fold half: this is the position of the elbow along the reference curves, it’s always half the amount of Fold Parameter. it has the following formula: Fold parameter / 2.
  6. opened: this is a parameter between 0 and 100 that governs how “open” the panels is. it has no formula since this is the user input.
  7. openedRaw: this is the raw data that will be used in the other formulae, in other words this is the mapped version of Opened. it’s calculated using this formula: (opened – 0) / (100 – 0) * (0.75 – 0) +0 (the zeros are for demonstration purposes, but please do keep in mind that (0 to 100) is the range of user input and (0 to 0.75) is the allowed input range of the panels. I have mapped it in order to further streamline the process)

Now that we have all the parameters set correctly, the panel should be able to respond to user input ( Opened parameter):

Step 2: programmatically applying the panels to face

In this step, we’ll be using a custom Dynamo package called Lunchbox that mostly deals with surface tesselation. dividing a face into quads is still doable without this tool but it would take a considerable amount of time and effort. so I really recommend that you use this workflow instead:

Programmatically subdividing our surface into quads and applying our panels
  1. Selecting the desired faces.
  2. Creating a list out of them.
  3. Using Panel.PanelQuad from Lunchbox to tessellate the faces with the desired U and V values.
  4. Flattening the list with an amount of 2.
  1. feeding that list into AdaptiveComponent.ByPoints node in order to place our panels.
  2. Sometimes we need to shift the indices (to rotate the panels) or revers the list (to mirror the panels) based on the context.

Step 3: Collecting date, time and Geo-location data from Revit

In this stage and the next one, we’ll heavily rely on a custom set of Dynamo tools Called Ladybug. we’ll be using it to generate a sun path diagram within dynamo. this will enable us to conduct complex solar studies for our project. but before that could happen, we’ll have to extract some vital information from the model first:

Generating the location is relatively easy, all we have to do is to use a Construct Location node from Ladybug and feed the required information into it using built-in Dynamo for Revit nodes. the only problem here is that there is no readily available node to acquire the time zone. so we’ll have to resort to using some python code to do that. this is the required python script:

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

#extracting time zone information from sunSettings
sunSettings = IN[0]
timezone = sunSettings.InternalElement.TimeZone

OUT = timezone

Now that we have our location information, it’s time start working on our date and time data. for that we’ll be using a SunSettings.CurrentDateTime node. once we a have DateTime object, the rest is just a matter of extracting the right kinds of information using some more python:

# Enable Python support and load DesignScript library
import clr
#extracting the month
dateTime = IN[0]
month = dateTime.Month
OUT = month
#extracting the day
dateTime = IN[0]
day = dateTime.Day
OUT = day
#extracting the hour
dateTime = IN[0]
hour = dateTime.Hour
OUT = hour
#extracting the minute
dateTime = IN[0]
min = dateTime.Minute
OUT = min

Now we’re able to feed all of that newly acquired information into a Calculate HOY node from Ladybug and we’re ready to conduct our solar studies.

Step 4: Generating sun path and calculating sun’s angle of incident

  1. Here we use our Location and HOY information to generate a sun path diagram using Ladybug’s Sunpath node.
  2. We normalize the sun’s directional vector by dissolving it into it’s X and Y components and regenerating another vector with zero as it’s Z component.
Generated sun path diagram within Dynamo, we also acquired sun’s directional vector.

Step 5: Acquiring orientation data for every instance of our panel

Everything we’ve done up until now is useless if we don’t know which way our panels are facing, there are many ways to tackle this problem, finding the panel’s centroid and then extracting the normal of the original face at that point is a perfectly viable option, but I wanted something more universal, I didn’t want to deal with the surface on which our panels are hosted since that can get really laborious really fast. a better alternative is to store every panel’s orientation information in it’s geometry. remember we created a tiny 10mm geometrical line when modeling our panels? were you wondering what it’s purpose was? well, this is exactly why we created that line in the first place, to find out the panel’s orientation later on. now you might be asking why couldn’t we just process the whole geometry for every instance of the panels and use a normal vector on one of it’s forward oriented faces? well technically we could do that, but as you can imagine that would take a really long time to run, since we’d have to process a whole bunch of geometry, even those that we don’t really need. obviously this is not a very efficient way of solving this problem. therefore we’ll dive a little deeper into the Revit API in order to extract the line geometry only.

Orientation information of every panel
  1. Prompting the user to select a single instance of the panel.
  2. Finding the right family type for the selected element.
  3. Acquiring every instance belonging to the selected family type.
  4. flattening the list.
  1. Pyhton Code: looping through every element and acquiring it’s Curve based geometry only.
  2. Getting the start and end point of every line.
  3. generating vectors representing the orientation of every panel using Vector.ByTwoPoints.

Here is the Python code used to extract Curve based geometry from the panels:

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

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

elements = IN[0]
lines = []

for element in elements:
	element = UnwrapElement(element)
	opt = Options()
	geo1 = element.get_Geometry(opt)
	enum1 = geo1.GetEnumerator() ; enum1.MoveNext()
	geo2 = enum1.Current.GetInstanceGeometry()

	for obj in geo2:	
		if isinstance(obj, Curve):
			lines.append(obj.ToProtoType())

OUT = lines

Step 6: Adjusting the “openness” parameter of every panel based on their angle of incidence with the rays of the sun

Now that we have the sun’s direction as well as the direction of every shading device, we can easily calculate the angle that they have with one another. we’ll later map this angle to the minimum and maximum values that the Opened parameter can accept (0 and 100). this way we can adjust every panel based on how much of an angle it has with the normalized vector of the sun.

  1. calculate the angle between the sun’s directional vector and the orientation of the panels using Vector.AngleWithVector.
  2. get the minimum and maximum angles and map the remaining values to somewhere between 0 and 100 using Math.MapTo.
  3. edit the Opened parameter of the elements based on the values that our solar studies have generated.

Every time the user changes one of these parameters in the Revit model: location of the project, date and/or time of the day, the facade updates itself to better correspond to those changes.

Selecting Erbil, Iraq as our Project location inside of Reivt

We selected Erbil, Iraq as our project location for this example and generated results for the following hours of the day on July 22nd 2020:

  • 7:00 am
  • 9:30 am
  • 12:00 pm
  • 2:30 pm
  • 5:00 pm
  • 7:30 pm

As you can see It’s now extremely easy and efficient to generate different results with a few simple clicks. with this we come to the end of this blog post, I really hope that you gained something useful from this long read and that this will serve as an inspiration for you to keep on creating!

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!

Parametric facade design from Image file using Revit and dynamo

In this blog post we’re going to use some fundamental Revit and Dynamo skills as well as a builtin Dynamo image processing library to create a workflow that converts an ordinary image into an amazing architectural facade in a matter of minuets (or even seconds depending on your hardware). we’re going to be using curtain walls and custom parametric curtain panels to model this. thus naturally creating the curtain panel family is the first step. after that we’ll learn how to use an image processing library inside of dynamo in order to extract all kinds of useful data from the image file. and last but not least, we’ll use that extracted pixel information to parametrically alter the facade in cool and interesting ways. so without further ado lets get started!

So the first step is to create the parametric curtain panel, the design and geometry for this element are rather simple since we will be placing thousands of these and we really don’t want to push our hardware too far (geometrical detail in the individual family instances is not that important anyways since each instance of this family represents a single pixel which together they create the final impression collectively).

So we’ll start by creating a new family, we will use the template “Metric Curtain Wall Panel”. now we’re good to go… open the exterior view and start by changing the distance between the reference lines (we want to have a 500mm x 500mm panel). once that is done create an extrusion. while in sketch mode, create a rectangular shape and constrain it to the existing reference lines as shown in the image bellow. then draw another rectangle within the first rectangle. we need to constrain this one using an instance parameter. add dimensions to the second rectangle as shown in the second image below, and associate those dimensions with an instance parameter. lets call this parameter “width” for now. once those steps are done, click create form. it’s a good idea to keep the extrusion end at around 100mm.

Constraining the first rectangle to the reference lines…
Constraining the inner rectangle with an instance parameter (width)…

You could also try and create another 20mm extrusion and constrain it to the inner rectangle to represent the glass but we’ll keep things simple for this demonstration. create a new architectural project and import the new family into it and we’re good to go. now that the custom panel family is complete we can move on to the next step and work on our curtain panel. for this demonstration we’ll be using a 50m x 50m curtain wall with 0.5m x 0.5m panels, effectively giving us 10,000 panels (pardon the unrealistic dimensions, this is for educational purposes only). after selecting our custom panel family as our curtain wall panels, we can move on to dynamo.


An image processing library will allow us to manipulate image files and extract valuable data from them. luckily for us, Dynamo happens to be very flexible in this regard, since it allows us to easily import .Net framework libraries and tools, we can even write custom nodes for Dynamo that use external C# libraries using Zero touch (more on this topic soon). but for the purposes of this tutorial we can use a builtin Dynamo node to extract the pixel data as shown in the graph bellow.

Extracting data from selected Image file (note that the image is 100 x 100 pixels) Click Here for high resolution.
  1. Get the file path to the image file
  2. Get file from file path
  3. Read image from file
  4. Get pixel data from image
  1. Get brightness of each pixel
  2. Map the values to our panel’s “width” min and max (0.02 and 0.24 in this case).
  3. Reverse the list

The graph above should be simple enough, I trust that you can easily follow through with it. now that we have extracted pixel brightness values and stored them in a 2 dimensional list (100 lists each containing 100 pixels), we’re ready to move on to the final stage of assigning those values to the relevant panels. selecting all the panels is easy enough but we need some simple list manipulation to get the lists of panels correspond to our data from the image. the following graph does just that.

Selecting and sorting all the panels in this project (Click Here for high resolution)
  1. Selecting the panel family type
  2. Getting all instances of the family type (10,000)
  3. Get the bounding box
  4. Get it’s centroid
  1. Python code: getting the Z coordinate
  2. Using “GetItemAtIndex” and “Chop” nodes in conjunction with the Z and X coordinates to sort the panels.

The python code block simply loops through the list of points to get their Z coordinates. of course just like the X coordinate, Z coordinate could also be obtained easily using a builtin dynamo node, but I thought that this was a good opportunity to demonstrate the freedom that the python code block offers us. so using it is completely optional, here’s the code:

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

#creating variables to store the lists of points
pts = IN[0]
z = []

#looping through the list of points and getting their Z component
for pt in pts:
    z.append(pt.Z)

#outputing a list of Z components 
OUT = z

Now all that is left is to assign the values to the panels using a “SetParameterByName” node:


Hit run in dynamo and…. voilà! the facade is complete! I really hope that this tutorial was somewhat useful and that you have enjoyed reading this blog post. keep creating and don’t forget to give something back to the universe!

Done!

The image has been accurately transferred onto our facade!

Introduction

Hello and welcome to CodedBIM! my name is Nasim Naji, I’m an architect from Iraqi Kurdistan and I’ve been working in the field of computational design for a while now. I have created countless custom workflows and solutions to aid me in my day to day challenges as an architect throughout my college years and beyond. I’m also a self taught C# and python programmer who thinks programming can help absolutely anyone and everyone to be more productive. and in some cases, even help them achieve results which are otherwise extremely time consuming, risky or outright impossible. even though I’ve occasionally used the knowledge in some rather cute personal projects outside of my field and profession, but the fact of the matter is, I mainly use software development and computer programming to aid me in my architectural career.

I have always felt that, the main strength of BIM, and it’s biggest advantage over traditional CAD solutions lies in the way it stores, handles and manipulates information in a raw and realistic way, and with the high level of complexity that BIM solutions such as Revit offer, I naturally had a desire to be able to freely and efficiently manipulate the model in meaningful ways. I first realized this fact when I started using an outstanding Autodesk product called Dynamo.


Dynamo’s node based user friendly interface can be used by architects and everyone else with minimum coding background….

Dynamo is a visual programming and non-destructive computational geometry modeling software which is available both as a standalone and an addin for Autodesk Revit and some other Autodesk products. with the innovative and user friendly node-based user interface of dynamo, I was introduced to the amazing world of visual programming and I can daringly say that I could never go back to using Revit without Dynamo. it has helped me tremendously over the years, both in school activities that were meant to be mostly artistic, as well as strictly realistic and accurate real world projects…

As amazing as dynamo is, and as much as I think that it helps smoothen out the learning curve of programming for AEC and it makes it easier to get into topics such as computational design, generative design, parametric architecture etc… but there is only so much that a visual programming tool can do. after using Dynamo for a while it quickly became obvious to me that learning a traditional software programming language can be extremely useful. for those of you who don’t know, Dynamo allows the user to write python code within it, so that’s where I started. one of the greatest advantages of writing python within Dynamo was the fact that now I had access to the Revit API and I could talk to Revit directly (if you don’t know what that is, Revit API is a set of Revit tools and functionalities that the developers have exposed for us to program). after learning python which is a relatively simple, but an extremely powerful language nevertheless, I slowly worked my way up from there, and I started experimenting with another more strongly typed language called C#, which quickly became my favorite language for writing Revit plugins and custom Dynamo nodes.


Now that you, the dear reader, know a little more about me and my past experiences, this brings us to CodedBIM and it’s purpose. CodedBIM is my first blog ever, and the main motivational force behind it’s creation is to serve as a platform, on which I can share my experiences in this field with the world, and hopefully potentially help others along the way. I really hope that you’ll enjoy your stay on my weblog and that we’ll be able to bring about an amazing community of professionals who are willing to excel in their work through exploring different alternatives and interdisciplinary collaboration.

Thank you very much for your time…

Nasim Naji Salim