Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
In this section, we introduce the essential Nodes available in the Dynamo Library that will help you create your own visual program like a pro.
Geometry for Computational Design: How do I work with geometric elements in Dynamo? Explore multiple ways to create simple or complex geometries from primitives.
The Building Blocks of Programs: What is "Data" and what are some fundamental types I can start using in my programs? Also, learn more about incorporating math and logic operations in your design workflow.
Designing with Lists: How do I manage and coordinate my data structures? Understand more about the concept of List and use it to manage your design data efficiently.
Dictionaries in Dynamo: What are Dictionaries? Find out how to use dictionaries to look up specific data and values from existing results.
Dynamo is an active open-source development project. Find out the list of software that supports Dynamo.
Dynamo comes pre-installed with software such as Revit3D, FormIt, Civil3D and etc.
For more guidance on using Dynamo with a specific software, we recommend referring to the following sections:
If you would like to use Dynamo as a standalone application. Continue reading for guidance on downloading the Sandbox.
The Dynamo application is available from the Dynamo website. Both official, past or pre-released versions are available from the download page. Visit Get Dynamo page and Click Download for the official released version.
If you are looking for previous or 'bleeding edge' development releases, all versions can be found in the lower section from the same page.
'Bleeding edge' development may include some new and experimental features that are yet to be fully tested, hence may be unstable. By using this, you may discover bugs or issues, help us improve the application by reporting issues to our team.
Beginners are advised to download the official stable release.
Before launching any version you have downloaded, you are required to unzip the content to your chosen folder.
Download and install 7zip to your computer for this step.
Right-click on the zip file and select Extract All...
Choose a destination to unzip all the files.
In your destination folder, double-click on DynamoSandbox.exe to launch it
You will see the DynamoSandbox startup screen as follow.
Congratulations, you have now finished the setup for using DynamoSandbox!
Geometry is an additional functionality in Dynamo Sandbox that is only available to users who have a current subscription or license to the following Autodesk software: Revit, Robot Structural Analysis, FormIt, and Civil 3D. Geometry allows users to import, create, edit and export geometry from Dynamo Sandbox.
The User Interface (UI) for Dynamo is organized into five main regions. We will briefly cover the overview here and further explain the Workspace and Library in the following sections.
Menus
Toolbar
Library
Workspace
Execution bar
Here are Menus for basic functionality of the Dynamo application. Like most Windows software, the first two menus related to managing files, operations for selection and content editing. The remaining menus are more specific to Dynamo.
General info and settings can be found on the Dynamo drop down menu.
About - Find out the Dynamo version installed on your machine.
Agreement to Collect Usability Data - This allows you to opt-in or out for sharing your user data to improve Dynamo.
Preferences - Includes settings such as define the application's decimal point precision and geometry render quality.
Exit Dynamo
If you're stuck, check out the Help Menu. You may access one of the Dynamo reference websites through your internet browser.
Getting Started - A brief introduction to using Dynamo.
Interactive Guides -
Samples - Reference example files.
Dynamo Dictionary - Resource with documentation on all nodes.
Dynamo Website - View the Dynamo Project on GitHub.
Dynamo Project Wiki - Visit the wiki for learning about development using the Dynamo API, supporting libraries and tools.
Display Start Page - Return to the Dynamo start page when within a document.
Report A Bug - Open an Issue on GitHub.
Dynamo's Toolbar contains a series of buttons for quick access to working with files as well as Undo [Ctrl + Z] and Redo [Ctrl + Y] commands. On the far right is another button that will export a snapshot of the workspace, which is extremely useful for documentation and sharing.
The Dynamo Library is a collection of functional libraries, each Library containing Nodes grouped by Category. It consists basic libraries which are added during default installation of Dynamo, as we continue to introduce its usage, we will demonstrate how to extend the base functionality with Custom Nodes and additional Packages. The Library section will cover a more detailed guidance on using it.
The Workspace is where we compose our visual programs, you may also change its Preview setting to view the 3D geometries from here. Refer Workspace for more details.
Run your Dynamo script from here. Click the dropdown icon on the Execution button to change between the different modes.
Automatic: Runs your script automatically. Changes are updated in realtime.
Manual: Script only runs when the 'Run' button is clicked. Useful for making changes to a complicated and 'heavy' script.
Periodic: This option is grayed out by default. Only available when the DateTime.Now node is used. You can set the graph to run automatically at a specified interval.
This Primer includes chapters developed with Mode Lab. These chapters focus on the essentials you will need to get up and running developing your own visual programs with Dynamo and key insights on how to take Dynamo further.
This guide is designed to cater to readers from different backgrounds and skill levels. General introduction about Dynamo setup, user interface and key concepts can be found in the following sections, we recommend new users to cover the following topics:
For users who would like to develop a more in-depth understanding of each element such as a specific Nodes and the concept behind it, we cover the fundamentals in its own chapter.
If you would like to see the demonstration of Dynamo workflows, we have included some graphs in the Sample Workflows section. Follow the attached instructions to create your own Dynamo graphs.
There are more topic specific exercises can be found in later chapters as we cover different topics about Dynamo. Exercises can usually be found in the last section of each page.
Dynamo wouldn't be what it is without a strong group of avid users and active contributors. Engage the community by following the Blog, adding your work to the Gallery, or discussing Dynamo in the Forum.
Dynamo is envisioned as a visual programming tool for designers, allowing us to make tools that make use of external libraries or any Autodesk product that has an API. With Dynamo Sandbox we can develop programs in a "Sandbox" style application - but the Dynamo ecosystem continues to grow.
The source code for the project is open-source, enabling us to extend its functionality to our hearts content. Check out the project on GitHub and browse the Works in Progress of users customizing Dynamo.
Browse, Fork, and start extending Dynamo for your needs
Dynamo is an open source visual programming platform for designers.
You have just opened the Dynamo Primer, a comprehensive guide to visual programming in Autodesk Dynamo. This primer is an on-going project to share the fundamentals of programming. Topics include working with computational geometry, best practices for rules-based design, cross-disciplinary programming applications, and more with the Dynamo Platform.
The power of Dynamo can be found in a wide variety of design-related activities. Dynamo enables an expanding list of readily accessible ways for you to get started:
Explore visual programming for the first time
Connect workflows in various software
Engage an active community of users, contributors, and developers
Develop an open-source platform for continued improvement
In the midst of this activity and exciting opportunity for working with Dynamo, we need a document of the same caliber, the Dynamo Primer.
Refer to the primer user guide to find out what you can expect to learn from this primer.
We are continuously improving Dynamo, so some features may look different from what is represented in this Primer. However, all functionality changes will be correctly represented.
The Dynamo Primer project is open source! We're dedicated to providing quality content and appreciate any feedback you may have. If you would like to report an issue on anything at all, please post them on our GitHub issue page: https://github.com/DynamoDS/DynamoPrimer/issues
If you would like to contribute a new section, edits, or anything else to this project, check out the GitHub repo to get started: https://github.com/DynamoDS/DynamoPrimer.
The Dynamo Primer is an open-source project, initiated by Matt Jezyk and the Dynamo Development team at Autodesk.
Mode Lab was commissioned to write the First Edition of the Primer. We thank them for all of their efforts in establishing this valuable resource.
John Pierson of Parallax Team was commissioned to update the Primer to reflect the Dynamo 2.0. revisions.
Matterlab was commissioned to update the Primer to reflect the Dynamo 2.13. revisions.
Archilizer was commissioned to update the primer to reflect the Dynamo 2.17. revisions.
Wood Rodgers was commissioned to update the Primer with content for Dynamo for Civil 3D.
A special thanks to Ian Keough for initiating and guiding the Dynamo project.
Thank you to Matt Jezyk, Ian Keough, Zach Kron, Racel Amour and Colin McCrone for enthusiastic collaboration and the opportunity to participate on a wide array of Dynamo projects.
Dynamo Please refer to the following sites for the most current stable release of Dynamo.
http://dynamobim.com/download/ or http://dynamobuilds.com
*Note: Starting with Revit 2020, Dynamo is bundled with Revit releases, resulting in manual installation not being required. More information is available at this blog post.
DynamoBIM The best source for additional information, learning content, and forums is the DynamoBIM website.
http://dynamobim.org
Dynamo GitHub Dynamo is an open-source development project on GitHub. To contribute, check out DynamoDS.
https://github.com/DynamoDS/Dynamo
Contact Let us know about any issues with this document.
Dynamo@autodesk.com
Copyright 2023 Autodesk
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
The Library contains all of the loaded Nodes, including the ten default categories Nodes that come with the installation as well as any additionally loaded Custom Nodes or Packages. The Nodes in the Library are organized hierarchically within libraries, categories, and, where appropriate, subcategories.
Basic Nodes: Comes with default installation.
Custom Nodes: Store your frequently used routines or special graph as Custom Nodes. You can also share your Custom Nodes with the community
Nodes from the Package Manager: Collection of published Custom Nodes.
We will go through the hierarchy of Nodes categories, show how you can search quickly from the library and learn about some of the frequently used Nodes among them.
Browsing through these categories is the fastest way to understand the hierarchy of what we can add to our Workspace and the best way to discover new Nodes you haven't used before.
Browse the Library by clicking through the menus to expand each category and its subcategory
Geometry are great menus to begin exploring as they contain the largest quantity of Nodes.
Library
Category
Subcategory
Node
These further categorize the Nodes among same subcategory based on whether the Nodes Create data, execute an Action, or Query data.
Hover your mouse over a Node to reveal more detailed information beyond its name and icon. This offers us a quick way to understand what the Node does, what it will require for inputs, and what it will give as an output.
Description - plain language description of the Node
Icon - larger version of the icon in the Library Menu
Input(s) - name, data type, and data structure
Output(s) - data type and structure
If you know with relative specificity which Node you want to add to your Workspace, type in the Search field to look up all matching Nodes.
Choose by clicking on the Node you wish to add or hit Enter to add highlighted nodes to the center of the Workspace.
Beyond using keywords to try to find Nodes, we can type the hierarchy separated with a period in the Search Field or with Code Blocks (which use the Dynamo textual language).
The hierarchy of each library is reflected in the Name of Nodes added to the Workspace.
Typing in different portions of the Node's place in the Library hierarchy in the library.category.nodeName
format returns different results
library.category.nodeName
category.nodeName
nodeName
or keyword
Typically the Name of the Node in the Workspace will be rendered in the category.nodeName
format, with some notable exceptions particularly in the Input and View Categories.
Beware of similarly named Nodes and note the category difference:
Nodes from most libraries will include the category format
Point.ByCoordinates
and UV.ByCoordinates
have the same Name but come from different categories
Notable exceptions include Built-in Functions, Core.Input, Core.View, and Operators
With hundreds of Nodes included in the basic installation of Dynamo, which ones are essential for developing our Visual Programs? Let's focus on those that let us define our program's parameters (Input), see the results of a Node's action (Watch), and define inputs or functionality by way of a shortcut (Code Block).
Input Nodes are the primary means for the User of our Visual Program - be that yourself or someone else - to interface with the key parameters. Here are some available from the Core Library:
Boolean
Number
String
Number Slider
Directory Path
Integer Slider
File Path
The Watch Nodes are essential to managing the data that is flowing through your Visual Program. You can view the result of a Node through the Node data preview by hovering your mouse over the node.
It will be useful to keep it revealed in a Watch Node
Or see the geometry results through a Watch3D Node.
Both of these are found in the View Category in the Core Library.
Tip: Occasionally the 3D Preview can be distracting when your Visual Program contains a lot of Nodes. Consider unchecking the Showing Background Preview option in the Settings Menu and using a Watch3D Node to preview your geometry.
Code Block Nodes can be used to define a block of code with lines separated by semi-colons. This can be as simple as X/Y
.
We can also use Code Blocks as a shortcut to defining a Number Input or call to another Node's functionality. The syntax to do so follows the Naming Convention of the Dynamo textual language, DesignScript.
Here is a simple demonstration (with instructions) for using Code Block in your script.
Double-click to create a Code Block Node
Type Circle.ByCenterPointRadius(x,y);
Click on Workspace to clear the selection should add x
and y
inputs automatically.
Create a Point.ByCoordinates Node and a Number Slider then connect them to the inputs of the Code Block.
The result of the executing the Visual Program is shown as the circle in the 3D Preview
The Dynamo Workspace consists of four main elements.
All Active Tabs.
Preview Mode
Zoom/Pan Controls
Node in Workspace
When you open a new file, a new Home Workspace will be opened by default.
You may create a Custom Node and open it in a Custom Node Workspace.
Only one Home Workspace is allowed in each Dynamo window but you may have multiple Custom Node Workspaces opened in tabs.
There are 3 methods to switch between different previews:
a. Using the top right icons
b. Right-click in Workspace
Switch from 3D Preview to Graph Preview
Switch from Graph Preview to 3D Preview
c. Using keyboard shortcut (Ctrl+B)
You may use icons or a mouse to navigate in either workspace.
a. In Graph Preview Mode
Using icons:
Using mouse:
Left-click - Select
Left-click and drag - Selection box to select multiple nodes
Middle scroll up/down - Zoom in/out
Middle-click and drag - Pan
Right-click anywhere on canvas - Open In-Canvas Search
b. In 3D Preview Mode
Using icons:
Using mouse:
Middle scroll up/down - Zoom in/out
Middle-click and drag - Pan
Right-click and drag - Orbit
Left-click to select any Node.
To select multiple Nodes, Click and drag to create a selection box.
In Dynamo, Nodes are the objects you connect to form a Visual Program. Each Node performs an operation - sometimes that may be as simple as storing a number or it may be a more complex action such as creating or querying geometry.
Most Nodes in Dynamo are composed of five parts. While there are exceptions, such as Input Nodes, the anatomy of each Node can be described as follows:
Name - The Name of the Node with a
Category.Name
naming conventionMain body - The main body of the Node - Right-clicking here presents options at the level of the whole Node
Ports (In and Out) - The receptors for Wires that supply the input data to the Node as well as the results of the Node's action
Default Value - Right-click on an input Port - some Nodes have default values that can be used or not used.
Lacing Icon - Indicates the Lacing option specified for matching list inputs (more on that later)
The Inputs and Outputs for Nodes are called Ports and act as the receptors for Wires. Data comes into the Node through Ports on the left and flows out of the Node after it has executed its operation on the right.
Ports expect to receive data of a certain type. For instance, connecting a number such as 2.75 to the Ports on a Point By Coordinates Node will successfully result in creating a Point; however, if we supply "Red" to the same Port it will result in an error.
Tip: Hover over a Port to see a tooltip containing the data type expected.
Port Label
Tool Tip
Data Type
Default Value
Dynamo gives an indication of the state of the execution of your Visual Program by rendering Nodes with different color schemes based on each Node's status. The hierarchy of states follows this sequence: Error > Warning > Info > Preview.
Hovering or right-clicking over the Name or Ports presents additional information and options.
Satisfied inputs - A node with blue vertical bars over its input ports is well-connected and has all of its inputs successfully connected.
Unsatisfied inputs – A node with a red vertical bar over one or more input ports needs to have those inputs connected.
Function – A node that outputs a function and has a gray vertical bar over an output port is a function node.
Selected - Currently selected nodes have an aqua highlight around their border.
Frozen - A translucent blue node is frozen, suspending the execution of the node.
Warning - A yellow status bar underneath the node indicates Warning state, meaning the node either lacks input data or may have incorrect data types.
Error - A red status bar underneath the node indicates that the node is in an Error state.
Info - Blue status bar underneath the node indicates Info state, which flags useful information about nodes. This state can be triggered when approaching a maximum value supported by the node, using a node in a way that has potential performance impacts, etc.
Tip: With this tooltip information in hand, examine the upstream Nodes to see if the data type or data structure required is in error.
Warning Tooltip - "Null" or no data cannot be understood as a Double, i.e., a number
Use the Watch Node to examine the input data
Upstream the Number Node is storing "Red," not a number
In some situations, you may want to prevent the execution of specific nodes in your Visual Program. You can do this by "freezing" the node, which is an option under the node's right-click menu.
Freezing a node also freezes the nodes that are downstream of it. In other words, all the nodes that depend on the output of a frozen node will also be frozen.
Wires connect between Nodes to create relationships and establish the Flow of our Visual Program. We can think of them literally as electrical wires that carry pulses of data from one object to the next.
Wires connect the output Port from one Node to the input Port of another Node. This directionality establishes the Flow of Data in the Visual Program.
Input Ports are on the left side and the Output Ports are located on the right side of Nodes, hence, we can generally say that the Program Flow moves from left to right.
Create a Wire by left-click on a Port subsequently left-click on the port of another Node to create a connection. While we are in the process of making a connection, the Wire will appear dashed and will snap to become solid lines when successfully connected.
The data will always flow through this Wire from output to input; however, we may create the wire in either direction in terms of the sequence of clicking on the connected Ports.
Frequently we will want to adjust the Program Flow in our Visual Program by editing the connections represented by the Wires. To edit a Wire, left click on the input Port of the Node that is already connected. You now have two options:
Change connection to an input Port, left-click on another input Port
To remove the Wire, pull the Wire away and left-click on Workspace
Reconnect multiple wires using Shift+left-click
Duplicate a wire using Ctrl+left-click
By default, our Wires will be previewed with a gray stroke. When a Node is selected, it will render any connecting Wire with the same aqua highlight as the Node.
Highlighted Wire
Default Wire
Hide Wires by Default
In case you prefer to hide the Wires in your graph, you can find this option from View > Connectors > untick Show Connectors.
With this setting, only the selected Nodes and its joining Wires will be shown in faint aqua highlight.
You can also hide selected wire only by Right-clicking on the Nodes output > select Hide Wires
Dynamo is a visual programming application that can be downloaded and run in either stand-alone "Sandbox" mode or as a plug-in for other software like Revit, FormIt or Civil 3D.
Learn more about the difference between Dynamo Core/Revit/Sandbox.
Dynamo enables us to work within a Visual Programming process wherein we connect elements together to define the relationships and the sequences of actions that compose custom algorithms. We can use our algorithms for a wide array of applications, from processing data to generating geometry, all in real-time and without writing a lick of code
.
Nodes and Wires are the key components in Dynamo to support a visual programming process. It help establish strong visual and systemic relationships between the parts of a design. Using simple mouse-click to connect the Nodes easily while developing and optimizing your design workflow.
From using Visual Programming for project workflows to developing customized tools, Dynamo is an integral aspect of a wide variety of exciting applications.
As a Visual Programming environment, Dynamo enables you to craft the way that data is processed. Data is numbers or text, but so is Geometry. As understood by the Computer, Geometry - or sometimes called Computational Geometry - is the data we can use to create beautiful, intricate, or performance-driven models. To do so, we need to understand the ins and outs of the various types of Geometry we can use.
Curves are the first Geometric Data Type we've covered that have a more familiar set of shape descriptive properties - How curvy or straight? How long or short? And remember that Points are still our building blocks for defining anything from a line to a spline and all the Curve types in between.
Line
Polyline
Arc
Circle
Ellipse
NURBS Curve
Polycurve
NURBS is a model used for representing curves and surfaces accurately. A sine curve in Dynamo using two different methods to create NURBS Curves to compare the results.
NurbsCurve.ByControlPoints uses the List of Points as Control Points
NurbsCurve.ByPoints draws a Curve through the List of Points
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
The term Curve is generally a catch-all for all different sort of curved (even if straight) shapes. Capital "C" Curve is the parent categorization for all of those shape types - Lines, Circles, Splines, etc. More technically, a Curve describes every possible Point that can be found by inputting "t" into a collection of functions, which may range from the simple (x = -1.26*t, y = t
) to functions involving calculus. No matter what kind of Curve we are working with, this Parameter called "t" is a property we can evaluate. Furthermore, regardless of the look of the shape, all Curves also have a start point and end point, which coincidentally align with the minimum and maximum t values used to create the Curve. This also helps us understand its directionality.
It's important to note that Dynamo assumes that the domain of "t" values for a Curve is understood to be 0.0 to 1.0.
All Curves also possess a number of properties or characteristics which can be used to describe or analyze them. When the distance between the start and end points is zero, the curve is "closed." Also, every curve has a number of control-points, if all these points are located in the same plane, the curve is "planar." Some properties apply to the curve as a whole, while others only apply to specific points along the curve. For example, planarity is a global property while a tangent vector at a given t value is a local property.
Lines are the simplest form of Curves. They may not look curvy but they are in fact Curves - just without any curvature. There are a few different ways to create Lines, the most intuitive being from Point A to Point B. The shape of the Line AB will be drawn between the points but mathematically it extends infinitely in both directions.
When we connect two Lines together, we have a Polyline. Here we have a straightforward representation of what a Control Point is. Editing any of these point locations will change the shape of the Polyline. If the Polyline is closed, we have a Polygon. If the Polygon's edge lengths are all equal, it is described as regular.
As we add more complexity to the Parametric Functions that define a shape, we can take one step further from a Line to create an Arc, Circle, Ellipse Arc, or Ellipse by describing one or two radii. The differences between the Arc version and the Circle or Ellipse is only whether or not the shape is closed.
NURBS (Non-uniform Rational Basis Splines) are mathematical representations that can accurately model any shape from a simple two dimensional Line, Circle, Arc, or Rectangle to the most complex three-dimensional free-form organic Curve. Because of their flexibility (relatively few control points, yet smooth interpolation based on Degree settings) and precision (bound by a robust math), NURBS models can be used in any process from illustration and animation to manufacturing.
Degree: The Degree of the Curve determines the range of influence the Control Points have on a Curve; where the higher the degree, the larger the range. The Degree is a positive whole number. This number is usually 1, 2, 3 or 5, but can be any positive whole number. NURBS lines and polylines are usually Degree 1 and most free-form Curves are Degree 3 or 5.
Control Points: The Control Points are a list of at least Degree+1 Points. One of the easiest ways to change the shape of a NURBS Curve is to move its Control Points.
Weight: Control Points have an associated number called a Weight. Weights are usually positive numbers. When a Curve’s Control Points all have the same weight (usually 1), the Curve is called non-rational, otherwise the Curve is called rational. Most NURBS curves are non-rational.
Knots: Knots are a list of (Degree+N-1) numbers, where N is the number of Control Points. The Knots are used together with the weights to control the influence of the Control Points on the resulting Curve. One use for Knots is to create kinks at certain points in the curve.
Degree = 1
Degree = 2
Degree = 3
Note that the higher the degree value, the more Control Points are used to interpolate the resulting Curve.
Vector is a representation of magnitude and direction, you can picture it as an arrow accelerating towards a particular direction at a given speed. It is a key component to our models in Dynamo. Note that, because they are in the Abstract category of "Helpers," when we create a Vector, we won't see anything in the Background Preview.
We can use a line as a stand in for a Vector preview.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Plane is a two dimensional surface, you can picture it as a flat surface that extends indefinitely. Each Plane has an Origin, X Direction, Y Direction, and a Z (Up) Direction.
Although they are abstract, Planes do have an origin position so we can locate them in space.
In Dynamo, Planes are rendered in the Background Preview.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Coordinate system is a system to determine the location of points or other geometric elements. The image below explains how it looks like in Dynamo and what each color represents.
Although they are abstract, Coordinate Systems also have an origin position so we can locate them in space.
In Dynamo, Coordinate Systems are rendered in the Background Preview as a point (origin) and lines defining the axes (X is red, Y is green, and Z is blue following convention).
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Vectors, Planes, and Coordinate Systems make up the primary group of Abstract Geometry Types. They help us define location, orientation, and the spatial context for other geometry that describe shapes. If I say that I'm in New York City at 42nd Street and Broadway (Coordinate System), standing on the street level (Plane), looking North (Vector), I've just used these "Helpers" to define where I am. The same goes for a phone case product or a skyscraper - we need this context to develop our model.
A vector is a geometric quantity describing Direction and Magnitude. Vectors are abstract; ie. they represent a quantity, not a geometrical element. Vectors can be easily confused with Points because they both are composed of a list of values. There is a key difference though: Points describe a position in a given coordinate system while Vectors describe a relative difference in position which is the same as saying "direction."
If the idea of relative difference is confusing, think of the Vector AB as "I'm standing at Point A, looking toward Point B." The direction, from here (A) to there (B), is our Vector.
Breaking down Vectors further into their parts using the same AB notation:
The Start Point of the Vector is called the Base.
The **End Point **of the Vector is called the Tip or the Sense.
Vector AB is not the same as Vector BA - that would point in the opposite direction.
If you're ever in need of comic relief regarding Vectors (and their abstract definition), watch the classic comedy Airplane and listen for the oft-quoted tongue-in cheek line:
Roger, Roger. What's our vector, Victor?
Planes are two-dimensional abstract "Helpers." More specifically, Planes are conceptually “flat,” extending infinitely in two directions. Usually they are rendered as a smaller rectangle near their origin.
You might be thinking, "Wait! Origin? That sounds like a Coordinate System... like the one I use to model in my CAD software!"
And you're correct! Most modeling software take advantage of construction planes or "levels" to define a local two-dimensional context to draft in. XY, XZ, YZ -or- North, Southeast, Plan might sound more familiar. These are all Planes, defining an infinite "flat" context. Planes don't have depth, but they do help us describe direction as well -
If we are comfortable with Planes, we are a small step away from understanding Coordinate Systems. A Plane has all the same parts as a Coordinate System, provided it is a standard "Euclidean" or "XYZ" Coordinate System.
There are other, however, alternative Coordinate Systems such as Cylindrical or Spherical. As we will see in later sections, Coordinate Systems can also be applied to other Geometry types to define a position on that geometry.
Add alternative coordinate systems - cylindrical, spherical
In the field of computational modeling, Meshes are one of the most pervasive forms of representing 3D geometry. Mesh geometry is generally made of a collection of quadrilaterals or triangles, it can be a light-weight and flexible alternative to working with NURBS, and Meshes are used in everything from rendering and visualizations to digital fabrication and 3D printing.
Dynamo defines Meshes using a Face-Vertex data structure. At its most basic level, this structure is simply a collection of points which are grouped into polygons. The points of a Mesh are called vertices, while the surface-like polygons are called faces.
To create a Mesh we need a list of vertices and a system of grouping those vertices into faces called an index group.
List of vertices
List of index groups to define faces
Dynamo's mesh capabilities can be extended by installing the Mesh Toolkit package. The Dynamo Mesh Toolkit provides tools to import Meshes from external file formats, create a Mesh from Dynamo geometry objects, and manually build Meshes by their vertices and indices.
The library also provides tools to modify Meshes, repair Meshes, or extract horizontal slices for use in fabrication.
Visit Mesh Toolkit case studies for example on using this package.
A Mesh is a collection of quadrilaterals and triangles that represents a surface or solid geometry. Like Solids, the structure of a Mesh object includes vertices, edges, and faces. There are additional properties that make Meshes unique as well, such as normals.
Mesh vertices
Mesh edges *Edges with only one adjoining face are called "Naked." All other edges are "Clothed"
Mesh faces
The vertices of a Mesh are simply a list of points. The index of the vertices is very important when constructing a Mesh, or getting information about the structure of a Mesh. For each vertex, there is also a corresponding vertex normal (vector) which describes the average direction of the attached faces and helps us understand the "in" and "out" orientation of the Mesh.
Vertices
Vertex Normals
A face is an ordered list of three or four vertices. The “surface” representation of a Mesh face is therefore implied according to the position of the vertices being indexed. We already have the list of vertices that make up the Mesh, so instead of providing individual points to define a face, we simply use the index of the vertices. This also allows us to use the same vertex in more than one face.
A quad face made with indices 0, 1, 2, and 3
A triangle face made with indices 1, 4, and 2 Note that the index groups can be shifted in their order - as long as the sequence is ordered in a counter-clockwise manner, the face will be defined correctly
How is Mesh geometry different from NURBS geometry? When might you want to use one instead of the other?
In a previous chapter, we saw that NURBS surfaces are defined by a series of NURBS curves going in two directions. These directions are labeled U
and V
, and allow a NURBs surface to be parameterized according to a two-dimensional surface domain. The curves themselves are stored as equations in the computer, allowing the resulting surfaces to be calculated to an arbitrarily small degree of precision. It can be difficult, however, to combine multiple NURBS surfaces together. Joining two NURBS surfaces will result in a polysurface, where different sections of the geometry will have different UV parameters and curve definitions.
Surface
Isoparametric (Isoparm) Curve
Surface Control Point
Surface Control Polygon
Isoparametric Point
Surface Frame
Mesh
Naked Edge
Mesh Network
Mesh Edges
Vertex Normal
Mesh Face / Mesh Face Normal
Meshes, on the other hand, are comprised of a discrete number of exactly defined vertices and faces. The network of vertices generally cannot be defined by simple UV
coordinates, and because the faces are discrete the amount of precision is built into the Mesh and can only be changed by refining the Mesh and adding more faces. The lack of mathematical descriptions allows Meshes to more flexibly handle complex geometry within a single Mesh.
Another important difference is the extent to which a local change in Mesh or NURBS geometry affects the entire form. Moving one vertex of a Mesh only affects the faces that are adjacent to that vertex. In NURBS surfaces, the extent of the influence is more complicated and depends on the degree of the surface as well as the weights and knots of the control points. In general, however, moving a single control point in a NURBS surface creates a smoother, more extensive change in geometry.
NURBS Surface - moving a control point has influence that extends across the shape
Mesh geometry - moving a vertex has influence only on adjacent elements
One analogy that can be helpful is to compare a vector image (composed of lines and curves) with a raster image (composed of individual pixels). If you zoom into a vector image, the curves remain crisp and clear, while zooming into a raster image results in seeing individual pixels become larger. In this analogy, NURBS surfaces can be compared to a vector image because there is a smooth mathematical relationship, while a Mesh behaves similarly to a raster image with a set resolution.
If we want to construct more complex models that cannot be created from a single surface or if we want to define an explicit volume, we must now venture into the realm of Solids (and Polysurfaces). Even a simple cube is complex enough to need six surfaces, one per face. Solids give access to two key concepts that Surfaces do not - a more refined topological description (faces, edges, vertices) and Boolean operations.
You can use Boolean operations to modify solids. Let's use a few Boolean operations to create a spiky ball.
Sphere.ByCenterPointRadius: Create the base Solid.
Topology.Faces, Face.SurfaceGeometry: Query the faces of the Solid and convert to surface geometry—in this case, the Sphere has only one Face.
Cone.ByPointsRadii: Construct cones using points on the surface.
Solid.UnionAll: Union the Cones and the Sphere.
Topology.Edges: Query the edges of the new Solid
Solid.Fillet: Fillet the Edges of the spiky ball
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Boolean operations are complex and can be slow to calculate. You can use the "freeze" functionality to suspend the execution of selected nodes and affected downstream nodes.
1.Use the right-click contextual menu to Freeze the Solid Union operation
2. The selected node and all downstream nodes will preview in a light grey ghosted mode, and affected wires will be displayed as dashed lines. The affected geometry preview will also be ghosted. You can now change values upstream without calculating the boolean union.
3. To unfreeze the nodes, right-click and uncheck Freeze.
4. All affected nodes and associated geometry previews will update and revert to the standard preview mode.
You can read more about freezing nodes in the Nodes and Wires section.
Solids consist of one or more Surfaces that contain volume by way of a closed boundary that defines "in" or "out." Regardless of how many of these Surfaces there are, they must form a "watertight" volume to be considered a Solid. Solids can be created by joining Surfaces or Polysurfaces together or by using operations such as loft, sweep, and revolve. Sphere, Cube, Cone and Cylinder primitives are also Solids. A Cube with at least one face removed counts as a Polysurface, which has some similar properties, but it is not a Solid.
A Plane is made of a single Surface and is not a Solid.
A Sphere is made of one Surface but is a Solid.
A Cone is made of two surfaces joined together to make a Solid.
A Cylinder is made of three surfaces joined together to make a Solid.
A Cube is made of six surfaces joined together to make a Solid.
Solids are made up of three types of elements: Vertices, Edges, and Faces. Faces are the surfaces that make up the Solid. Edges are the Curves that define the connection between adjacent faces, and vertices are the start and end points of those Curves. These elements can be queried using the Topology nodes.
Faces
Edges
Vertices
Solids can be modified by filleting or chamfering their edges to eliminate sharp corners and angles. The chamfer operation creates a ruled surface between two faces, while a fillet blends between faces to maintain tangency.
Solid Cube
Chamfered Cube
Filleted Cube
Solid Boolean operations are methods for combining two or more Solids. A single Boolean operation actually means performing four operations:
Intersect two or more objects.
Split them at the intersections.
Delete unwanted portions of the geometry.
Join everything back together.
Union: Remove the overlapping portions of the Solids and join them into a single Solid.
Difference: Subtract one Solid from another. The Solid to be subtracted is referred to as a tool. Note that you could switch which Solid is the tool to keep the inverse volume.
Intersection: Keep only the intersecting volume of the two Solids.
UnionAll: Union operation with sphere and outward-facing cones
DifferenceAll: Difference operation with sphere and inward-facing cones
We use Surface in model to represent objects we see in our three dimensional world. While Curves are not always planar ie. they are three dimensional, the space they define is always bound to one dimension. Surfaces give us another dimension and a collection of additional properties we can use within other modeling operations.
Import and evaluate a Surface at a Parameter in Dynamo to see what kind of information we can extract.
Surface.PointAtParameter returns the Point at a given UV Coordinate
Surface.NormalAtParameter returns the Normal Vector at a given UV Coordinate
Surface.GetIsoline returns the Isoparametric Curve at a U or V Coordinate - note the isoDirection input.
Download the example files by clicking on the link below.
A full list of example files can be found in the Appendix.
A Surface is a mathematical shape defined by a function and two parameters, Instead of t
for Curves, we use U
and V
to describe the corresponding parameter space. This means we have more geometrical data to draw from when working with this type of Geometry. For example, Curves have tangent vectors and normal planes (which can rotate or twist along the curve's length), whereas Surfaces have normal vectors and tangent planes that will be consistent in their orientation.
Surface
U Isocurve
V Isocurve
UV Coordinate
Perpendicular Plane
Normal Vector
Surface Domain: A surface domain is defined as the range of (U,V) parameters that evaluate into a three dimensional point on that surface. The domain in each dimension (U or V) is usually described as two numbers (U Min to U Max) and (V Min to V Max).
Although the shape of the Surface by not look "rectangular" and it locally may have a tighter or looser set of isocurves, the "space" defined by its domain is always two dimensional. In Dynamo, Surfaces are always understood to have a domain defined by a minimum of 0.0 and maximum of 1.0 in both U and V directions. Planar or trimmed Surfaces may have different domains.
Isocurve (or Isoparametric Curve): A curve defined by a constant U or V value on the surface and a domain of values for the corresponding other U or V direction.
UV Coordinate: The Point in UV Parameter Space defined by U, V, and sometimes W.
Perpendicular Plane: A Plane that is perpendicular to both U and V Isocurves at a given UV Coordinate.
Normal Vector: A Vector defining the direction of "up" relative to the Perpendicular Plane.
NURBS Surfaces are very similar to NURBS curves. You can think of NURBS Surfaces as a grid of NURBS Curves that go in two directions. The shape of a NURBS Surface is defined by a number of control points and the degree of that surface in the U and V directions. The same algorithms are used to calculate shape, normals, tangents, curvatures and other properties by way of control points, weights and degree.
In the case of NURBS surfaces, there are two directions implied by the geometry, because NURBS surfaces are, regardless of the shape we see, rectangular grids of control points. And even though these directions are often arbitrary relative to the world coordinate system, we will use them frequently to analyze our models or generate other geometry based on the Surface.
Degree (U,V) = (3,3)
Degree (U,V) = (3,1)
Degree (U,V) = (1,2)
Degree (U,V) = (1,1)
Polysurfaces are composed of Surfaces that are joined across an edge. Polysurfaces offer more than two dimensional UV definition in that we can now move through the connected shapes by way of their Topology.
While "Topology" generally describes a concept around how parts are connected and/or related Topology in Dynamo is also a type of Geometry. Specifically it is a parent category for Surfaces, Polysurfaces, and Solids.
Sometimes called patches, joining Surfaces in this manner allows us to make more complex shapes as well as define detail across the seam. Conveniently we can apply a fillet or chamfer operation to the edges of a Polysurface.
Once we are ready to dive deeper into developing Visual Programs, we will need a deeper understanding of the building blocks we will use. This chapter introduces fundamental concepts around data - the stuff that travels through the Wires of our Dynamo program.
Formally, a String is a sequence of characters representing a literal constant or some type of variable. Informally, a string is programming lingo for text. We've worked with numbers, both integers and decimal numbers, to drive parameters and we can do the same with text.
Strings can be used for a wide range of applications, including defining custom parameters, annotating documentation sets, and parsing through text-based data sets. The string Node is located in the Core>Input Category.
The sample Nodes above are strings. A number can be represented as a string, as can a letter, or an entire array of text.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
You can parse through large amounts of data quickly by querying strings. We'll talk about some basic operations which can speed up a workflow and help for software interoperability.
The image below considers a string of data coming from an external spreadsheet. The string represents the vertices of a rectangle in the XY-Plane. Let's break down some string split operations in miniature exercise:
The ";" separator splits each vertex of the rectangle. This creates a list with 3 items for each vertex.
By hitting the "+" in the middle of the Node, we create new separator.
Add a "," string to the canvas and plug in to the new separator input.
Our result is now a list of ten items. The Node first splits based on separator0, then based on separator1.
While the list of items above may look like numbers, they are still regarded as individual strings in Dynamo. In order to create points, their data type needs to be converted from a string to a number. This is done with the String.ToNumber Node
This Node is straightforward. Plug the String.Split results into the input. The output doesn't look different, but the data type is now a number instead of a string.
With some basic additional operations, we now have a triangle drawn at the origin based on the original string input.
Since a string is a generic text object, they host a wide range of applications. Let's take a look at some of the major actions in the Core>String Category in Dynamo:
This is a method of merging two strings together in order. This takes each literal string in a list and creates one merged string.
The following represents the concatenation of three strings:
Add or subtract strings to the concatenation by clicking the +/- buttons in the center of the Node.
The output gives one concatenated string, with spaces and punctuation included.
The join method is very similar to concatenate, except it has an added layer of punctuation.
If you've worked in Excel, you may have come across a CSV file. This stands for comma-separated values. One could use a comma (or in this case, two dashes) as the separator with the String.Join node in order to create a similar data structure.
The following image represents the joining of two strings:
The separator input allows one to create a string which divides the joined strings.
Let's begin with a basic string split of the stanza. We first notice that the writing is formatted based on commas. We'll use this format to separate each line into individual items.
The base string is pasted into a String Node.
Another String Node is used to denote the separator. In this case, we're using a comma.
A String.Split Node is added to the canvas and connected to the two strings.
The output shows that we've now separated the lines into individual elements.
Now, let's get to the good part of the poem: the last two lines. The original stanza was one item of data. We separated this data into individual items in the first step. Now we need to do a search for the text we're looking for. And while we can do this by selecting the last two items of the list, if this were an entire book, we wouldn't want to read through everything and manually isolate the elements.
Instead of manually searching, we use a String.Contains Node to perform a search for a set of characters. This is the similar to doing the "Find" command in a word processor. In this case, we get a return of "true" or "false" if that substring is found within the item.
In the searchFor input, we define a substring that we're looking for within the stanza. Let's use a String Node with the text "And miles".
The output gives us a list of falses and trues. We'll use this boolean logic to filter the elements in the next step.
List.FilterByBoolMask is the Node we want to use to cull out the falses and trues. The "in" output return the statements with a "mask" input of "true, while the "out" output return those which are "false".
Our output from the "in" is as expected, giving us the final two lines of the stanza.
Now, we want to drive home the repetition of the stanza by merging the two lines together. When viewing the output of the previous step, we notice that there are two items in the list:
Using two List.GetItemAtIndex Nodes, we can isolate the items using the values of 0 and 1 as the index input.
The output for each node gives us, in order, the final two lines.
To merge these two items into one, we use the String.Join Node:
After adding the String.Join Node, we notice that we need a separator.
To create the separator, we add a String Node to the canvas and type in a comma.
The final output has merged the last two items into one.
This may seem like a lot of work to isolate the last two lines; and it's true, string operations often require some up front work. But they are scalable, and they can be applied to large datasets with relative ease. If you are working parametrically with spreadsheets and interoperability, be sure to keep string operations in mind.
Geometry is the language for design. When a programming language or environment has a geometry kernel at its core, we can unlock the possibilities for designing precise and robust models, automating design routines, and generating design iterations with algorithms.
Additionally, making models in Dynamo and connecting the preview of what we see in the Background Preview to the flow of data in our graph should become more intuitive over time.
Note the assumed coordinate system rendered by the grid and colored axes
Selected Nodes will render the corresponding geometry (if the Node creates geometry) in the background the highlight color
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Geometry, traditionally defined, is the study of shape, size, relative position of figures, and the properties of space. This field has a rich history going back thousands of years. With the advent and popularization of the computer, we gained a powerful tool in defining, exploring, and generating geometry. It is now so easy to calculate the result of complex geometric interactions, the fact that we are doing so is almost transparent.
If you're curious to see how diverse and complex geometry can get using the power of your computer, do a quick web search for the Stanford Bunny - a canonical model used to test algorithms.
Understanding geometry in the context of algorithms, computing, and complexity, may sound daunting; however, there are a few key, and relatively simple, principles that we can establish as fundamentals to start building towards more advanced applications:
Geometry is Data - to the computer and Dynamo, a Bunny not all that different from a number.
Geometry relies on Abstraction - fundamentally, geometric elements are described by numbers, relationships, and formulas within a given spatial coordinate system
Geometry has a Hierarchy - points come together to make lines, lines come together to make surfaces, and so on
Geometry simultaneously describes both the Part and the Whole - when we have a curve, it is both the shape as well as all the possible points along it
In practice, these principles mean that we need to be aware of what we are working with (what type of geometry, how was it created, etc.) so that we can fluidly compose, decompose, and recompose different geometries as we develop more complex models.
Let's take a moment to look at the relationship between the Abstract and Hierarchical descriptions of Geometry. Because these two concepts are related, but not always obvious at first, we can quickly arrive at a conceptual roadblock once we start developing deeper workflows or models. For starters, let's use dimensionality as an easy descriptor of the "stuff" we model. The number of dimensions required to describe a shape gives us a window into how Geometry is organized hierarchically.
A Point (defined by coordinates) doesn't have any dimensions to it - it's just numbers describing each coordinate
A Line (defined by two points) now has one dimension - we can "walk" the line either forward (positive direction) or backward (negative direction)
A Plane (defined by two lines) has two dimensions - walking more left or more right is now possible
A Box (defined by two planes) has three dimensions - we can define a position relative to up or down
Dimensionality is a convenient way to start categorizing Geometry but it's not necessarily the best. After all, we don't model with only Points, Lines, Planes, and Boxes - what if I want something curvy? Furthermore, there is a whole other category of Geometric types that are completely abstract ie. they define properties like orientation, volume, or relationships between parts. We can't really grab a hold of a Vector so how do we define it relative to what we see in space? A more detailed categorization of the geometric hierarchy should accommodate the difference between Abstract Types or "Helpers," each of which we can group by what they help do and types that help describe the shape of model elements.
Creating models in Dynamo is not limited to what we can generate with Nodes. Here are some key ways to take your process to the next level with Geometry:
Dynamo allows you to import files - try using a CSV for point clouds or SAT for bringing in surfaces
When working with Revit, we can reference Revit elements to use in Dynamo
Logic, or more specifically, Conditional Logic, allows us to specify an action or set of actions based on a test. After evaluating the test, we will have a Boolean value representing True
or False
that we can use to control the Program Flow.
Numeric variables can store a whole range of different numbers. Boolean variables can only store two values referred to as True or False, Yes or No, 1 or 0. We rarely use booleans to perform calculations because of their limited range.
The "If" statement is a key concept in programming: "If this is true, then that happens, otherwise something else happens. The resulting action of the statement is driven by a boolean value. There are multiple ways to define an "If" statement in Dynamo:
Let's go over a brief example on each of these three nodes in action using the conditional "If" statement.
In this image, the boolean is set to true, which means that the result is a string reading: "this is the result if true". The three Nodes creating the If statement are working identically here.
Again, the Nodes are working identically. If the boolean is changed to false, our result is the number Pi, as defined in the original If statement.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Let's use logic to separate a list of numbers into a list of even numbers and a list of odd numbers.
a. Number Range - add a number range to the canvas.
b. Numbers - add three number nodes to the canvas. The value for each number node should be: 0.0 for start, 10.0 for end, and 1.0 for step.
c. Output - our output is a list of 11 numbers ranging from 0-10.
d. Modulo (%)- Number Range into x and 2.0 into y. This calculates the remainder for each number in the list divided by 2. The output from this list gives us a list of values alternating between 0 and 1.
e. Equality Test (==) - add an equality test to the canvas. Plug modulo output into the x input and 0.0 into the y input.
f. Watch - The output of the equality test is a list of values alternating between true and false. These are the values used to separate the items in the list. 0 (or true) represents even numbers and (1, or false) represents odd numbers.
g. List.FilterByBoolMask - this Node will filter the values into two different lists based on the input boolean. Plug the original number range into the list input and the equality test output into the mask input. The in output represents true values while the out output represents false values.
h. Watch - as a result, we now have a list of even numbers and a list of odd numbers. We've used logical operators to separate lists into patterns!
Building off of the logic established in the first exercise, let's apply this setup into a modeling operation.
2. We'll jump off from the previous exercise with the same Nodes. The only exceptions (in addition to changing the format are):
a. Use a Sequence Node with these input values.
b. We've unplugged the in list input into List.FilterByBoolMask. We'll put these Nodes aside for now, but they'll come in handy later in the exercise.
3. Let's begin by creating a separate group of Graph as shown in the image above. This group of Nodes represents a parametric equation to define a line curve. A few notes:
a. The first Number Slider represents the frequency of the wave, it should have a min of 1, a max of 4, and a step of 0.01.
b. The second Number Slider represents the amplitude of the wave, should have a min of 0, a max of 1, and a step of 0.01.
c. PolyCurve.ByPoints - if the above Node diagram is copied, the result is a sine curve in the Dynamo Preview viewport.
The method here for the inputs: use number nodes for more static properties and number sliders on the more flexible ones. We want to keep the original number range that we're defining in the beginning of this step. However, the sine curve that we create here should have some flexibility. We can move these sliders to watch the curve update its frequency and amplitude.
4. We're going to jump around a bit in the definition, so let's look at the end result so that we can reference what we're getting at. The first two steps are made separately, we now want to connect the two. We'll use the base sine curve to drive the location of the zipper components, and we'll use the true/false logic to alternate between little boxes and larger boxes.
a. Math.RemapRange - Using the number sequence created in step 02, let's create a new series of numbers by remapping the range. The original numbers from step 01 range from 0-100. These numbers range from 0 to 1 by the newMin and newMax inputs respectively.
5. Create a Curve.PointAtParameter Node, then connect the Math.RemapRange output from step 04 as its param input.
This step creates points along the curve. We remapped the numbers to 0 to 1 because the input of param is looking for values in this range. A value of 0 represents the start point, a value of 1 represents the end points. All numbers in between evaluate within the [0,1] range.
6. Connect the output from Curve.PointAtParameter to the List.FilterByBoolMask to separate the list of odd and even indices.
a. List.FilterByBoolMask - Plug Curve.PointAtParameter from the previous step into the list input.
b. Watch - a watch node for in and a watch node for out shows that we have two lists representing even indices and odd indices. These points are ordered in the same way on the curve, which we demonstrate in the next step.
7. Next, we are going to use the output result from List.FilterByBoolMask in step 05 to generate geometries with sizes according to its indices.
Cuboid.ByLengths - recreate the connections seen in the image above to get a zipper along the sine curve. A cuboid is just a box here, and we're defining its size based on the curve point in the center of the box. The logic of the even/odd divide should now be clear in the model.
a. List of cuboids at even indices.
b. List of cuboids at odd indices.
Voila! You have just programmed a process of defining the geometry dimensions according to the logic operation demonstrated in this exercise.
Now that we've established what a list is, let's talk about operations we can perform on it. Imagine a list as a deck of playing cards. A deck is the list and each playing card represents an item.
What queries can we make from the list? This accesses existing properties.
Number of cards in the deck? 52.
Number of suits? 4.
Material? Paper.
Length? 3.5" or 89mm.
Width? 2.5" or 64mm.
What actions can we perform on the list? This changes the list based on a given operation.
We can shuffle the deck.
We can sort the deck by value.
We can sort the deck by suit.
We can split the deck.
We can partition the deck by dealing out individual hands.
We can select a specific card in the deck.
All of the operations listed above have analogous Dynamo nodes for working with lists of generic data. The lessons below will demonstrate some of the fundamental operations we can perform on lists.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
The image below is the base graph which we are drawing lines between two circles to represent basic list operations. We'll explore how to manage data within a list and demonstrate the visual results through the list actions below.
Begin with a Code Block with a value of
500;
Plug into the x input of a Point.ByCoordinates node.
Plug the node from the previous step into the origin input of a Plane.ByOriginNormal node.
Using a Circle.ByPlaneRadius node, plug the node from the previous step into the plane input.
Using Code Block, designate a value of
50;
for the radius. This is the first circle we'll create.With a Geometry.Translate node, move the circle up 100 units in the Z direction.
With a Code Block node, define a range of ten numbers between 0 and 1 with this line of code:
0..1..#10;
Plug the code block from the previous step into the param input of two Curve.PointAtParameter nodes. Plug Circle.ByPlaneRadius into the curve input of the top node, and Geometry.Translate into the curve input of the node beneath it.
Using a Line.ByStartPointEndPoint, connect the two Curve.PointAtParameter nodes.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
The List.Count node is straightforward: it counts the number of values in a list and returns that number. This node gets more nuanced as we work with lists of lists, but we'll demonstrate that in the coming sections.
The **List.Count ****** node returns the number of lines in the Line.ByStartPointEndPoint node. The value is 10 in this case, which agrees with the number of points created from the original Code Block node.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
List.GetItemAtIndex is a fundamental way to query an item in the list.
First, Right click on Line.ByStartPointEndPoint node to switch off its preview.
Using the List.GetItemAtIndex node, we are selecting index "0", or the first item in the list of lines.
Change slider value between 0 and 9 to select different item using List.GetItemAtIndex.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
List.Reverse reverses the order of all of the items in a list.
To properly visualize the reversed list of lines, create more lines by changing the Code Block to
0..1..#50;
Duplicate the Line.ByStartPointEndPoint node, insert a List.Reverse node in between Curve.PointAtParameter and the second Line.ByStartPointEndPoint
Use Watch3D nodes to preview two different results. The first one shows the result without a reversed list. The lines connect vertically to neighboring points. The reversed list, however, will connect all of the points to the opposing order in the other list.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
List.ShiftIndices is a good tool for creating twists or helical patterns, or any other similar data manipulation. This node shifts the items in a list a given number of indices.
In the same process as the reverse list, insert a List.ShiftIndices into the Curve.PointAtParameter and Line.ByStartPointEndPoint.
Using a Code Block, designated a value of "1" to shift the list one index.
Notice that the change is subtle, but all of the lines in the lower Watch3D node have shifted one index when connecting to the other set of points.
By changing to Code Block to a larger value, "30" for example, we notice a significant difference in the diagonal lines. The shift is working like a camera's iris in this case, creating a twist in the original cylindrical form.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
List.FilterByBooleanMask will remove certain items based on a list of booleans, or values reading "true" or "false".
In order to create a list of values reading "true" or "false", we need to a little more work...
Using a Code Block, define an expression with the syntax:
0..List.Count(list);
. Connect the Curve.PointAtParameter node to the list input. We'll walk through this setup more in the code block chapter, but the line of code in this case is giving us a list representing each index of the Curve.PointAtParameter node.Using a %** (modulus)** node, connect the output of the code block into the x input, and a value of 4 into the y input. This will give us the remainder when dividing the list of indices by 4. Modulus is a really helpful node for pattern creation. All values will read as the possible remainders of 4: 0, 1, 2, 3.
From the %** (modulus)** node, we know that a value of 0 means that the index is divisible by 4 (0,4,8,etc...). By using a == node, we can test for the divisibility by testing it against a value of "0".
The Watch node reveals just this: we have a true/false pattern which reads: true,false,false,false....
Using this true/false pattern, connect to the mask input of two List.FilterByBooleanMask nodes.
Connect the Curve.PointAtParameter node into each list input for the List.FilterByBooleanMask.
The output of Filter.ByBooleanMask reads "in" and "out". "In" represents values which had a mask value of "true" while "out" represents values which had a value of "false". By plugging the "in" outputs into the startPoint and endPoint inputs of a Line.ByStartPointEndPoint node, we've created a filtered list of lines.
The Watch3D node reveals that we have fewer lines than points. We've selected only 25% of the nodes by filtering only the true values!
Lists are the way we organize data. On your computer's operating system, you have files and folders. In Dynamo, we can regard these as items and lists, respectively. Like your operating system, there are many ways to create, modify, and query data. In this chapter, we'll break down how lists are managed in Dynamo.
The most common kind of Point in Dynamo exists in our three-dimensional World Coordinate System and has three coordinates [X,Y,Z] (3D Point in Dynamo).
A 2D Point in Dynamo has two coordinates [X,Y].
Parameters for both Curves and Surfaces are continuous and extend beyond the edge of the given geometry. Since the shapes that define the Parameter Space reside in a three-dimensional World Coordinate System, we can always translate a Parametric Coordinate into a "World" Coordinate. The point [0.2, 0.5] on the surface for example is the same as point [1.8, 2.0, 4.1] in world coordinates.
Point in assumed World XYZ Coordinates
Point relative to a given Coordinate System (Cylindrical)
Point as UV Coordinate on a Surface
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
If Geometry is the language of a model, then Points are the alphabet. Points are the foundation upon which all other geometry is created - we need at least two Points to create a Curve, we need at least three Points to make a Polygon or a Mesh Face, and so on. Defining the position, order, and relationship among Points (try a Sine Function) allows us to define higher order geometry like things we recognize as Circles or Curves.
A Circle using the functions
x=r*cos(t)
andy=r*sin(t)
A Sine Curve using the functions
x=(t)
andy=r*sin(t)
Points can exist in a two-dimensional Coordinate System as well. Convention has different letter notation depending upon what kind of space we are working with - we might be using [X,Y] on a Plane or [U,V] if we are on a surface.
A Point in Euclidean Coordinate System: [X,Y,Z]
A Point in a Curve Parameter Coordinate System: [t]
A Point in a Surface Parameter Coordinate System: [U,V]
Color is a great data type for creating compelling visuals as well as for rendering difference in the output from your Visual Program. When working with abstract data and varying numbers, sometimes it's difficult to see what's changing and to what degree. This is a great application for colors.
Colors in Dynamo are created using ARGB inputs.This corresponds to the Alpha, Red, Green, and Blue channels. The alpha represents the transparency of the color, while the other three are used as primary colors to generate the whole spectrum of color in concert.
The colors in the table below query the properties used to define the color: Alpha, Red, Green, and Blue. Note that the Color.Components Node gives us all four as different outputs, which makes this Node preferable for querying the properties of a color.
The colors in the table below correspond to the HSB color space. Dividing the color into hue, saturation, and brightness is arguably more intuitive for how we interpret color: What color should it be? How colorful should it be? And how light or dark should the color be? This is the breakdown of hue, saturation, and brightness respectively.
The current Node works well, but it can be a little awkward to get everything working the first time around. The best way to become familiar with the color gradient is to test it out interactively. Let's do a quick exercise to review how to setup a gradient with output colors corresponding to numbers.
Define three colors: Using a Code Block node, define red, green, and blue by plugging in the appropriate combinations of 0 and 255.
Create list: Merge the three colors into one list.
Define Indices: Create a list to define the grip positions of each color (ranging from 0 to 1). Notice the value of 0.75 for green. This places the green color 3/4 of the way across the horizontal gradient in the color range slider.
Code Block: Input values (between 0 and 1) to translate to colors.
The Display.ByGeometry Node gives us the ability to color geometry in the Dynamo viewport. This is helpful for separating different types of geometry, demonstrating a parametric concept, or defining an analysis legend for simulation. The inputs are simple: geometry and color. To create a gradient like the image above, the color input is connected to the Color Range Node.
The Display.BySurfaceColors node gives us the ability to map data across a surface using color! This functionality introduces some exciting possibilities for visualizing data obtained through discrete analysis like solar, energy, and proximity. Applying color to a surface in Dynamo is similar to applying a texture to a material in other CAD environments. Let's demonstrate how to use this tool in the brief exercise below.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This exercise focuses on controlling color parametrically in parallel with geometry. The geometry is a basic helix, which we define below using the Code Block. This is a quick and easy way to create a parametric function; and since our focus is on color (rather than geometry), we use the code block to efficiently create the helix without cluttering the canvas. We will use the code block more frequently as the primer moves to more advanced material.
Code Block: Define the two code blocks with the formulas above. This is a quick parametric method for creating a spiral.
Point.ByCoordinates: Plug the three outputs from the code block into the coordinates for the Node.
We now see an array of points creating a helix. The next step is to create a curve through the points so that we can visualize the helix.
PolyCurve.ByPoints: Connect the Point.ByCoordinates output into the points input for the Node. We get a helical curve.
Curve.PointAtParameter: Connect the PolyCurve.ByPoints output into the curve input. The purpose of this step is to create a parametric attractor point which slides along the curve. Since the curve is evaluating a point at parameter, we'll need to input a param value between 0 and 1.
Number Slider: After adding to the canvas, change the min value to 0.0, the max value to 1.0, and the step value to .01. Plug the slider output into the param input for Curve.PointAtParameter. We now see a point along the length of the helix, represented by a percentage of the slider (0 at the start point, 1 at the end point).
With the reference point created, we now compare the distance from the reference point to the original points defining the helix. This distance value will drive geometry as well as color.
Geometry.DistanceTo: Connect Curve.PointAtParameter output into the input. Connect Point.ByCoordinates into the geometry input.
Watch: The resultant output shows a list of distances from each helical point to the reference point along the curve.
Our next step is to drive parameters with the list of distances from the helical points to the reference point. We use these distance values to define the radii of a series of spheres along the curve. In order to keep the spheres a suitable size, we need to remap the values for distance.
Math.RemapRange: Connect Geometry.DistanceTo output into the numbers input.
Code Block: connect a code block with a value of 0.01 into the newMin input and a code block with a value of 1 into the newMax input.
Watch: connect the Math.RemapRange output into one node and the Geometry.DistanceTo output into another. Compare the results.
This step has remapped the list of distance to be a smaller range. We can edit the newMin and newMax values however we see fit. The values will remap and will have the same distribution ratio across the domain.
Sphere.ByCenterPointRadius: connect the Math.RemapRange output into the radius input and the original Point.ByCoordinates output into the centerPoint input.
Change the value of the number slider and watch the size of the spheres update. We now have a parametric jig
The size of the spheres demonstrates the parametric array defined by a reference point along the curve. Let's use the same concept for the sphere radius to drive their color.
Color Range: Add top the canvas. When hovering over the value input, we notice that the numbers requested are between 0 and 1. We need to remap the numbers from the Geometry.DistanceTo output so that they are compatible with this domain.
Sphere.ByCenterPointRadius: For the time being, let's disable the preview on this node (Right Click > Preview)
Math.RemapRange: This process should look familiar. Connect the Geometry.DistanceTo output into the numbers input.
Code Block: Similar to an earlier step, create a value of 0 for the newMin input and a value of 1 for the newMax input. Notice that we are able to define two outputs from one code block in this case.
Color Range: Connect the Math.RemapRange output into the value input.
Color.ByARGB: This is what we'll do to create two colors. While this process may look awkward, it's the same as RGB colors in another software, we're just using visual programming to do it.
Code Block: create two values of 0 and 255. Plug the two outputs into the two Color.ByARGB inputs in agreement with the image above (or create your favorite two colors).
Color Range: The colors input requests a list of colors. We need to create this list from the two colors created in the previous step.
List.Create: merge the two colors into one list. Plug the output into the colors input for Color Range.
Display.ByGeometryColor: Connect Sphere.ByCenterPointRadius into the geometry input and the Color Range into the color input. We now have a smooth gradient across the domain of the curve.
If we change the value of the Number Slider from earlier in the definition, the colors and sizes update. Colors and radius size are directly related in this case: we now have a visual link between two parameters!
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
First, we need to create (or reference) a surface to use as an input for the Display.BySurfaceColors node. For this example we are lofting between a sine and cosine curve.
This group of nodes is creating points along the Z-axis then displacing them based on sine and cosine functions. The two point lists are then used to generate NURBS curves.
Surface.ByLoft: generate an interpolated surface between the list of NURBS curves.
File Path: select an image file to sample for pixel data downstream
use File.FromPath to convert the file path to a file then pass into Image.ReadFromFile to output an image for sampling
Image.Pixels: input an image and provide a sample value to use along the x and y dimensions of the image.
Slider: provide sample values for Image.Pixels
Display.BySurfaceColors: map array of color values across surface along X and Y respectively
Close-up preview of the output surface with resolution of 400x300 samples
Data is the stuff of our programs. It travels through Wires, supplying inputs for Nodes where it gets processed into a new form of output data. Let's review the definition of data, how it's structured, and begin using it in Dynamo.
Data is a set of values of qualitative or quantitative variables. The simplest form of data is numbers such as 0
, 3.14
, or 17
. But data can also be of a number of different types: a variable representing changing numbers (height
); characters (myName
); geometry (Circle
); or a list of data items (1,2,3,5,8,13,...
).
In Dynamo, we add/feed data to the input Ports of Nodes - we can have data without actions but we need data to process the actions that our Nodes represent. When we've added a Node to the Workspace, if it doesn't have any inputs supplied, the result will be a function, not the result of the action itself.
Simple Data
Data and Action (A Node) successfully executes
Action (A Node) without Data Inputs returns a generic function
Beware of Nulls The 'null'
type represents the absence of data. While this is an abstract concept, you will likely come across this while working with Visual Programming. If an action doesn't create a valid result, the Node will return a null.
Testing for nulls and removing nulls from data structure is a crucial part to creating robust programs.
When we are Visual Programming, we can very quickly generate a lot of data and require a means of managing its hierarchy. This is the role of Data Structures, the organizational schemes in which we store data. The specifics of Data Structures and how to use them vary from programming language to programming language.
In Dynamo, we add hierarchy to our data through Lists. We will explore this in depth in later chapters, but let's start simply:
A list represents a collection of items placed into one structure of data:
I have five fingers (items) on my hand (list).
There are ten houses (items) on my street (list).
A Number Sequence node defines a list of numbers by using a start, amount, and step input. With these nodes, we've created two separate lists of ten numbers, one which ranges from 100-109 and another which ranges from 0-9.
The List.GetItemAtIndex node selects an item in a list at a specific index. When choosing 0, we get the first item in the list (100 in this case).
Applying the same process to the second list, we get a value of 0, the first item in the list.
Now we merge the two lists into one by using the List.Create node. Notice that the node creates a list of lists. This changes the structure of the data.
When using List.GetItemAtIndex again, with index set to 0, we get the first list in the list of lists. This is what it means to treat a list as an item, which is somewhat different from other scripting languages. We will get more advanced with list manipulation and data structure in later chapters.
The key concept to understand about data hierarchy in Dynamo: with respect to data structure, lists are regarded as items. In other words, Dynamo functions with a top-down process for understanding data structures. What does this mean? Let's walk through it with an example.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this first example, we assemble a shelled cylinder which walks through the geometry hierarchy discussed in this section.
1.Add Point.ByCoordinates - after adding the node to canvas, we see a point at the origin of the Dynamo preview grid. The default values of the x,y, and z inputs are 0.0, giving us a point at this location.
2. Plane.ByOriginNormal - The next step in the geometry hierarchy is a plane. There are several ways to construct a plane, and we are using an origin and normal for the input. The origin is the point node created in the previous step.
Vector.ZAxis - this is a unitized vector in the z direction. Notice there are not inputs, only a vector of [0,0,1] value. We use this as the normal input for the Plane.ByOriginNormal node. This gives us a rectangular plane in the Dynamo preview.
3. Circle.ByPlaneRadius - Stepping up the hierarchy, we now create a curve from the plane in our previous step. After plugging into the node, we get a circle at the origin. The default radius on the node is value of 1.
4. Curve.Extrude - Now we make this thing pop by giving it some depth and going in the third dimension. This node creates a surface from a curve by extruding it. The default distance on the node is 1, and we should see a cylinder in the viewport.
5. Surface.Thicken - This node gives us a closed solid by offsetting the surface a given distance and closing the form. The default thickness value is 1, and we see a shelled cylinder in the viewport in line with these values.
6. Number Slider - Rather than using the default values for all of these inputs, let's add some parametric control to the model.
Domain Edit - after adding the number slider to the canvas, click the caret in the top left to reveal the domain options.
Min/Max/Step - change the min, max, and step values to 0,2, and 0.01 respectively. We are doing this to control the size of the overall geometry.
7. Number Sliders - In all of the default inputs, let's copy and paste this number slider (select the slider, hit Ctrl+C, then Ctrl+V) several times, until all of the inputs with defaults have a slider instead. Some of the slider values will have to be larger than zero to get the definition to work (ie: you need an extrusion depth in order to have a surface to thicken).
8. We've now created a parametric shelled cylinder with these sliders. Try to flex some of these parameters and see the geometry update dynamically in the Dynamo viewport.
Number Sliders - taking this a step further, we've added a lot of sliders to the canvas, and need to clean up the interface of the tool we just created. Right click on one slider, select "Rename..." and change each slider to the appropriate name for its parameter (thickness, Radius, Height, etc).
9. At this point, we've created an awesome thickening cylinder thing. This is one object currently, let's look at how to create an array of cylinders that remains dynamically linked. To do this, we're going to create a list of cylinders, rather than working with a single item.
Addition (+) - Our goal is to add a row of cylinders next to the cylinder we've created. If we want to add one cylinder adjacent to the current one, we need to consider both radius of the cylinder and the thickness of its shell. We get this number by adding the two values of the sliders.
10. This step is more involved so let's walk through it slowly: the end goal is to create a list of numbers which define the locations of each cylinder in a row.
a. Multiplication - First, we want to multiply the value from the previous step by 2. The value from the previous step represents a radius, and we want to move the cylinder the full diameter.
b. Number Sequence - we create an array of numbers with this node. The first input is the multiplication node from the previous step into the step value. The start value can be set to 0.0 using a number node.
c. Integer Slider - For the amount value, we connect an integer slider. This will define how many cylinders are created.
d. Output - This list shows us the distance moved for each cylinder in the array, and is parametrically driven by the original sliders.
11. This step is simple enough - plug the sequence defined in the previous step into the x input of the original Point.ByCoordinates. This will replace the slider pointX which we can delete. We now see an array of cylinders in the viewport (make sure the integer slider is larger than 0).
12. The chain of cylinders is still dynamically linked to all of the sliders. Flex each slider to watch the definition update!
If the simplest form of data is numbers, the easiest way to relate those numbers is through Mathematics. From simple operators like divide to trigonometric functions, to more complex formulas, Math is a great way to start exploring numeric relationships and patterns.
Operators are a set of components that use algebraic functions with two numeric input values, which result in one output value (addition, subtraction, multiplication, division, etc.). These can be found under Operators>Actions.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Combine operators and variables to form a more complex relationship through Formulas. Use sliders to make a Formula that can be controlled with input parameters.
1.Create Number sequence that represents the 't' in the parametric equation, so we want to use a list that's large enough to define a spiral.
Number Sequence: define a number sequence based on three inputs: start, amount and step.
2. The step above has created a list of numbers to define the parametric domain. Next, create group of Nodes represent the golden spiral equation.
The golden spiral is defined as the equation:
The image below represents the golden spiral in in visual programming form. When stepping through the group of Nodes, try to pay attention to the parallel between the visual program and written equation.
a. Number Slider: Add two number sliders to the canvas. These sliders will represent the a and the b variables of the parametric equation. These represent a constant which is flexible, or parameters which we can adjust towards a desired outcome.
b. Multiplication (*) : The multiplication Node is represented by an asterisk. We'll use this repeatedly to connect multiplying variables
c. Math.RadiansToDegrees: The 't' values need to be translated to degrees for their evaluation in the trigonometric functions. Remember, Dynamo defaults to degrees for evaluating these functions.
d. Math.Pow: as a function of the 't' and the number 'e' this creates the Fibonacci sequence.
e. Math.Cos and Math.Sin: These two trigonometric functions will differentiate the x-coordinate and the y-coordinate, respectively, of each parametric point.
f. Watch: We now see that our output is two lists, these will be the x and y coordinates of the points used to generate the spiral.
Point.ByCoordinates: Connect the upper multiplication node into the 'x' input and the lower into the 'y' input. We now see a parametric spiral of points on the screen.
Polycurve.ByPoints: Connect Point.ByCoordinates from the previous step into points. We can leave connectLastToFirst without an input because we aren't making a closed curve. This creates a spiral which passes through each point defined in the previous step.
We've now completed the Fibonacci Spiral! Let's take this further into two separate exercises from here, which we'll call the Nautilus and the Sunflower. These are abstractions of natural systems, but the two different applications of the Fibonacci spiral will be well represented.
Circle.ByCenterPointRadius: We'll use a circle Node here with the same inputs as the previous step. The radius value defaults to 1.0, so we see an immediate output of circles. It becomes immediately legible how the points diverge further from the origin.
Number Sequence: This is the original array of 't'. By plugging this into the radius value of Circle.ByCenterPointRadius, the circle centers are still diverging further from the origin, but the radius of the circles is increasing, creating a funky Fibonacci circle graph.
Bonus points if you make it 3D!
As a jumping-off point, let's start with the same step from the previous exercise: creating a spiral array of points with the Point.ByCoordinates Node.

Next, follow these mini steps to generate a series of spiral at various rotation.
a. Geometry.Rotate: There are several Geometry.Rotate options; be certain you've chosen the Node with geometry,basePlane, and degrees as its inputs. Connect Point.ByCoordinates into the geometry input. Right click on this Node and make sure the lacing is set to 'Cross Product'
b. Plane.XY: Connect to the basePlane input. We will rotate around the origin, which is the same location as the base of the spiral.
c. Number Range: For our degree input, we want to create multiple rotations. We can do this quickly with a Number Range component. Connect this into the degrees input.
d. Number: And to define the range of numbers, add three number nodes to the canvas in vertical order. From top to bottom, assign values of 0.0,360.0, and 120.0 respectively. These are driving the rotation of the spiral. Notice the output results from the Number Range node after connecting the three number nodes to the Node.
Our output is beginning to resemble a whirlpool. Let's adjust some of the Number Range parameters and see how the results change.
Change the step size of the Number Range node from 120.0 to 36.0. Notice that this is creating more rotations and is therefore giving us a denser grid.
Change the step size of the Number Range node from 36.0 to 3.6. This now gives us a much denser grid, and the directionality of the spiral is unclear. Ladies and gentlemen, we've created a sunflower.
A list is a collection of elements, or items. Take a bunch of bananas, for example. Each banana is an item within the list (or bunch). It's easier to pick up a bunch of bananas rather than each banana individually, and the same holds for grouping elements by parametric relationships in a data structure.
When we buy groceries, we put all of the purchased items into a bag. This bag is also a list. If we're making banana bread, we need 3 bunches of bananas (we're making a lot of banana bread). The bag represents a list of banana bunches and each bunch represents a list of bananas. The bag is a list of lists (two-dimensional) and the banana bunch is a list (one-dimensional).
In Dynamo, list data is ordered, and the first item in each list has an index "0". Below, we'll discuss how lists are defined in Dynamo and how multiple lists relate to one another.
One thing that might seem odd at first is that the first index of a list is always 0; not 1. So, when we talk about the first item of a list, we actually mean the item that corresponds to index 0.
For example, if you were to count the number of fingers we have on our right hand, chances are that you would have counted from 1 to 5. However, if you were to put your fingers in a list, Dynamo would have given them indices from 0 to 4. While this may seem a little strange to programming beginners, the zero-based index is standard practice in most computation systems.
Note that we still have 5 items in the list; it's just that the list is using a zero-based counting system. And the items being stored in the list don't just have to be numbers. They can be any data type that Dynamo supports, such as points, curves, surfaces, families, etc.
a. Index
b. Point
c. Item
Often times the easiest way to take a look at the type of data stored in a list is to connect a watch node to another node's output. By default, the watch node automatically shows all indices to the left side of the list and displays the data items on the right.
These indices are a crucial element when working with lists.
Pertaining to lists, inputs and outputs vary depending on the Dynamo node being used. As an example, let's use a list of 5 points and connect this output to two different Dynamo nodes: PolyCurve.ByPoints and Circle.ByCenterPointRadius:
The points input for PolyCurve.ByPoints is looking for "Point[]". This represents a list of points
The output for PolyCurve.ByPoints is a single polycurve created from a list of five point.
The centerPoint input for Circle.ByCenterPointRadius asks for "Point".
The output for Circle.ByCenterPointRadius is a list of five circles, whose centers correspond to the original list of points.
The input data for PolyCurve.ByPoints and Circle.ByCenterPointRadius are the same, however the Polycurve.ByPoints node gives us one polycurve while the Circle.ByCenterPointRadius node gives us 5 circles with centers at each point. Intuitively this makes sense: the polycurve is drawn as a curve connecting the 5 points, while the circles create a different circle at each point. So what's happening with the data?
Hovering over the points input for Polycurve.ByPoints, we see that the input is looking for "Point[]". Notice the brackets at the end. This represents a list of points, and to create a polycurve, the input needs to be a list for each polycurve. This node will therefore condense each list into one polycurve.
On the other hand, the centerPoint input for Circle.ByCenterPointRadius asks for "Point". This node looks for one point, as an item, to define the center point of the circle. This is why we get five circles from the input data. Recognizing these difference with inputs in Dynamo helps to better understand how the nodes are operating when managing data.
Data matching is a problem without a clean solution. It occurs when a node has access to differently sized inputs. Changing the data matching algorithm can lead to vastly different results.
Imagine a node which creates line segments between points (Line.ByStartPointEndPoint). It will have two input parameters which both supply point coordinates:
The simplest way is to connect the inputs one-on-one until one of the streams runs dry. This is called the “Shortest List” algorithm. This is the default behavior for Dynamo nodes:
The “Longest List” algorithm keeps connecting inputs, reusing elements, until all streams run dry:
Finally, the “Cross Product” method makes all possible connections:
As you can see there are different ways in which we can draw lines between these sets of points. Lacing options are found by right-clicking the center of a node and choosing the "Lacing" menu.
Imagine you have a bunch of grapes. If you wanted to make grape juice, you wouldn't squeeze each grape individually - you'd put them all through the juicer at once. Replication in Dynamo works in a similar way: instead of applying an operation to one item at a time, Dynamo can apply it to an entire list in one go.
Dynamo nodes automatically recognize when they're working with lists and apply their operations across multiple elements. This means you don't have to manually loop through items - it just happens. But how does Dynamo decide how to process lists when there's more than one?
There are two main ways:
Let's say you're in the kitchen, making fruit juices. You have a list of fruits: {apple, orange, pear}
and a fixed amount of water for each juice: 1 cup
. You want to make a juice with each fruit, using the same amount of water. In this case, Cartesian Replication comes into play.
In Dynamo, this means you're feeding the list of fruits into the fruit input of the Juice.Maker node, while the water input remains constant at 1 cup. The node then processes each fruit individually, combining it with the fixed amount of water. The result is:
apple juice with 1 cup of water
orange juice with 1 cup of water
pear juice with 1 cup of water
Each fruit is paired with the same amount of water.
Zip Replication works a little differently. If you had two lists, one for fruits: {apple, orange, pear}
and another for sugar amounts: {2 tbsp, 3 tbsp, 1 tbsp}
, Zip Replication would combine corresponding items from each list. For example:
apple juice with 2 tablespoons of sugar
orange juice with 3 tablespoons of sugar
pear juice with 1 tablespoon of sugar
Each fruit is paired with it's corresponding amount of sugar.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
To demonstrate the lacing operations below, we'll use this base file to define shortest list, longest list, and cross product.
We'll change the lacing on Point.ByCoordinates, but won't change anything else about the graph above.
Choosing shortest list as the lacing option (also the default option), we get a basic diagonal line composed of five points. Five points is the length of the lesser list, so the shortest list lacing stops after it reaches the end of one list.
By changing the lacing to longest list, we get a diagonal line which extends vertically. By the same method as the concept diagram, the last item in the list of 5 items will be repeated to reach the length of the longer list.
By changing the lacing to Cross Product, we get every combination between each list, giving us a 5x10 grid of points. This is an equivalent data structure to the cross product as shown in the concept diagram above, except our data is now a list of lists. By connecting a polycurve, we can see that each list is defined by its X-Value, giving us a row of vertical lines.
This index provides additional information on all the nodes used in this primer, as well as other components you might find useful. This is just an introduction to some of the 500 nodes available in Dynamo.
**In other words, if you create a Cuboid width (X-axis) length 10, and transform it to a CoordinateSystem with 2 times scaling in X, the width will still be 10. ASM does not allow you to extract the Vertices of a body in any predictable order, so it is impossible to determine the dimensions after a transform.
To start using, launch it from your toolbar panel. Depends on which software you are using, the launch icon can usually be found from the Menu > Manage Tab. Click on Dynamo icon to launch it.
New - Create a new .dyn file
Open - Open an existing .dyn (workspace) or .dyf (custom node) file
Save/Save As - Save your active .dyn or .dyf file
Undo - Undo your last action
Redo - Redo the next action
Export Workspace as Image - Export the visible workspace as a PNG file
Create: Create or construct a geometry from scratch. E.g a circle.
Action: Perform an action on an object. E.g scaling a circle.
Query: Get a property of an object that already exists. E.g get the radius of a circle.
Graph Preview
3D Preview
Zoom to fit
Zoom in
Zoom out
Pan
Zoom to fit
Zoom in
Zoom out
Pan
Orbit
Preview off - A gray status bar underneath the node and an eye icon indicate that geometry preview for the node is switched off.
If your Visual Program contains warning or errors, Dynamo will provide additional information about the problem. Any Node that is Yellow will also have a tooltip above the Name. Hover your mouse over the warning or error tooltip icon to expand it.
Line is made of a set of points, each line has at least 2 points. One of the most common way to create line in Dynamo is using Line.ByStartPointEndPoint
to create a Line in Dynamo.
This makes Solid Booleans a powerful time-saving process. There are three Solid Boolean operations that distinguish which parts of the geometry are kept.
In addition to these three operations, Dynamo has Solid.DifferenceAll and Solid.UnionAll nodes for performing difference and union operations with multiple Solids.
In this exercise, we're going to use methods of querying and manipulating strings to deconstruct the final stanza of Robert Frost's . Not the most practical application, but it will help us to grasp conceptual string actions as we apply them to legible lines of rhythm and rhyme.
Understanding the Geometry types and will allow us to navigate the collection of Geometry Nodes available to us in the Library. The Geometry Nodes are organized alphabetically as opposed to hierarchically - here they are displayed similar to their layout in the Dynamo interface.
The Dynamo Package Manager offers additional functionality for extended geometry types and operations - check out the package
Photo by
A is defined by nothing more than one or more values called coordinates. How many coordinate values we need to define the Point depends upon the Coordinate System or context in which it resides.
The color range is similar to the Remap Range Node from the exercise: it remaps a list of numbers into another domain. But instead of mapping to a number domain, it maps to a color gradient based on input numbers ranging from 0 to 1.
Now, the bulk of Nodes from the previous step will work fine, but it is a lot of work. To create a more efficient workflow, have a look at to define a string of Dynamo expressions into one node. In this next series of steps, we'll look at using the parametric equation to draw the Fibonacci spiral.
Pattern Now that we've made a circular Nautilus shell, let's jump into parametric grids. We're going to use a basic rotate on the Fibonacci Spiral to create a Fibonacci grid, and the result is modeled after the .
Photo by .
For a deeper dive into how this works, check out the .
While Dynamo was originally built with Revit in mind, its versatility as a visual programming tool extends beyond Revit. Dynamo is also integrated into Civil 3D, giving users the ability to create powerful automation routines for civil infrastructure projects. It is an extremely useful tool for processing anything from common tasks to the most complex design workflows, ultimately helping you save time, optimize your designs, and make better design decisions. Dynamo offers a whole suite of nodes specifically designed for Civil 3D, as well as third-party libraries from a thriving community.
This chapter of the Primer will focus on Dynamo for Civil 3D, starting with the basics and working up to more advanced topics.
If (If)
test, true, false
result
Code Block ((x?y:z);)
x? y, z
result
ARGB Color (Color.ByARGB)
A,R,G,B
color
Alpha (Color.Alpha)
color
A
Red (Color.Red)
color
R
Green (Color.Green)
color
G
Blue (Color.Blue)
color
B
Components (Color.Components)
color
A, R, G, B
Hue (Color.Hue)
color
Hue
Saturation (Color.Saturation)
color
Saturation
Brightness (Color.Brightness)
color
Brightness
Object.IsNull
obj
bool
Add (+)
var[]...[], var[]...[]
var[]...[]
Subtract (-)
var[]...[], var[]...[]
var[]...[]
Multiply (*)
var[]...[], var[]...[]
var[]...[]
Divide (/)
var[]...[], var[]...[]
var[]...[]
CREATE
Color.ByARGB Construct a color by alpha, red, green, and blue components.
Color Range Get a color from a color gradient between a start color and an end color.
ACTIONS
Color.Brightness Gets the brightness value for this color.
Color.Components Lists the components for the color in the order: alpha, red, green, blue.
Color.Saturation Gets the saturation value for this color
Color.Hue Gets the hue value for this color.
QUERY
Color.Alpha Find the alpha component of a color, 0 to 255.
Color.Blue Find the blue component of a color, 0 to 255.
Color.Green Find the green component of a color, 0 to 255.
Color.Red Find the red component of a color, 0 to 255.
CREATE
GeometryColor.ByGeometryColor Displays geometry using a color.
ACTIONS
View.Watch Visualize the output of node.
View.Watch 3D Shows a dynamic preview of geometry.
ACTIONS
Boolean Selection between a true and false.
Code Block Allows for DesignScript code to be authored directly.
Directory Path Allows you to select a directory on the system to get its path
File Path Allows you to select a file on the system to get its filename
Integer Slider A slider that produces integer values.
Number Creates a number.
Number Slider A slider that produces numeric values.
String Creates a string.
Object.IsNull Determines if the given object is null.
CREATE
List.Create Makes a new list out of the given inputs.
List.Combine Applies a combinator to each element in two sequences
Number Range Creates a sequence of numbers in the specified range
Number Sequence Creates a sequence of numbers.
ACTIONS
List.Chop Chop a list into a set of lists each containing the given amount of items.
List.Count Returns the number of items stored in the given list.
List.Flatten Flattens a nested list of lists by a certain amount.
List.FilterByBoolMask Filters a sequence by looking up corresponding indices in a separate list of booleans.
List.GetItemAtIndex Gets an item from the given list that's located at the specified index.
List.Map Applies a function over all elements of a list, generating a new list from the results
List.Reverse Creates a new list containing the items of the given list but in reverse order
List.ReplaceItemAtIndex Replace an item from the given list that's located at the specified index
List.ShiftIndices Shifts indices in the list to the right by the given amount
List.TakeEveryNthItem Fetches items from the given list at indices that are multiples of the given value, after the given offset.
List.Transpose Swaps rows and columns in a list of lists. If there are some rows that are shorter than others, null values are inserted as place holders in the resultant array such that it is always rectangular
ACTIONS
If Conditional statement. Checks the boolean value of the test input. If the test input is true, the result outputs the true input, otherwise the result outputs the false input.
ACTIONS
Math.Cos Fines the cosine of an angle.
Math.DegreesToRadians Converts an angle in degrees to an angle in radians.
Math.Pow Raises a number to the specified power.
Math.RadiansToDegrees Converts an angle in radians to an angle in degrees.
Math.RemapRange Adjusts the range of a list of numbers while preserving the distribution ratio.
Math.Sin Finds the sine of an angle.
Map Maps a value into an input range
ACTIONS
String.Concat Concatenates multiple strings into a single string.
String.Contains Determines if the given string contains the given substring.
String.Join Concatenates multiple strings into a single string, inserting the given separator between each joined string.
String.Split Divides a single string into a list of strings, with divisions determined by the given separator strings.
String.ToNumber Converts a string to an integer or a double.
CREATE
Circle.ByCenterPointRadius Creates a Circle with input center Point and radius in the world XY plane, with world Z as normal.
Circle.ByPlaneRadius Create a Circle centered at the input Plane origin (root), lying in the input Plane, with given radius.
CREATE
CoordinateSystem.ByOrigin Create a CoordinateSystem with origin at input Point, with X and Y Axes set as WCS X and Y axes
CoordinateSystem.ByCylindricalCoordinates Creates a CoordinateSystem at the specified cylindrical coordinate parameters with respect to the specified coordinate system
CREATE
Cuboid.ByLengths Create a Cuboid centered at WCS origin, with width, length, and height.
Cuboid.ByLengths (origin)
Create a Cuboid centered at input Point, with specified width, length, and height.
Cuboid.ByLengths (coordinateSystem)
Create a Cuboid centered at WCS origin, with width, length, and height.
Cuboid.ByCorners
Create a Cuboid spanning from low Point to high Point.
Cuboid.Length
Return the input dimensions of the Cuboid, NOT the actual world space dimensions. **
Cuboid.Width
Return the input dimensions of the Cuboid, NOT the actual world space dimensions. **
Cuboid.Height
Return the input dimensions of the Cuboid, NOT the actual world space dimensions. **
BoundingBox.ToCuboid
Get the Bounding Box as a solid Cuboid
ACTIONS
Curve.Extrude (distance) Extrudes a Curve in the normal Vector direction.
Curve.PointAtParameter Get a Point on the Curve at a specified parameter between StartParameter() and EndParameter().
ACTIONS
Geometry.DistanceTo Obtain the distance from this Geometry to another.
Geometry.Explode Separates compound or non-separated elements into their component parts
Geometry.ImportFromSAT List of imported geometries
Geometry.Rotate (basePlane) Rotates an object around the Plane origin and normal by a specified degree.
Geometry.Translate Translates any geometry type by the given distance in the given direction.
CREATE
Line.ByBestFitThroughPoints Creates a Line best approximating a scatter plot of Points.
Line.ByStartPointDirectionLength Create a straight Line starting at Point, extending in Vector direction by specified length.
Line.ByStartPointEndPoint Creates a straight Line between two input Points.
Line.ByTangency Create a Line tangent to the input Curve, positioned at the parameter Point of the input Curve.
QUERY
Line.Direction The direction of the Curve.
Create
NurbsCurve.ByControlPoints Create a BSplineCurve by using explicit control points.
NurbsCurve.ByPoints Create a BSplineCurve by interpolating between points
Create
NurbsSurface.ByControlPoints Create a NurbsSurface by using explicit control Points with specified U and V degrees.
NurbsSurface.ByPoints Creates a NurbsSurface with specified interpolated points and U and V degrees. The resultant surface will pass through all of the points.
CREATE
Plane.ByOriginNormal Create a Plane centered at root Point, with input normal Vector.
Plane.XY Creates a plane in the world XY
CREATE
Point.ByCartesianCoordinates Form a Point in the given coordinate system with 3 cartesian coordinates
Point.ByCoordinates (2d) Form a Point in the XY plane given two 2 Cartesian coordinates. The Z component is 0.
Point.ByCoordinates (3d) Form a Point given 3 Cartesian coordinates.
Point.Origin Get the Origin point (0,0,0)
ACTIONS
Point.Add Add a vector to a point. The same as Translate (Vector).
QUERY
Point.X Get the X component of a point
Point.Y Get the Y component of a point
Point.Z Get the Z component of a point
CREATE
Polycurve.ByPoints Make PolyCurve from sequence of lines connecting points. For closed curve last point should be in the same location as the start point.
CREATE
Rectangle.ByWidthLength (Plane) Create a Rectangle centered at input Plane root, with input width (Plane X axis length) and (Plane Y axis length).
CREATE
Sphere.ByCenterPointRadius Create a Solid Sphere centered at the input Point, with given radius.
CREATE
Surface.ByLoft Create a Surface by lofting between input cross section Curves
Surface.ByPatch Create a Surface by filling in the interior of a closed boundary defined by input Curves.
ACTIONS
Surface.Offset Offset Surface in direction of Surface normal by specified distance
Surface.PointAtParameter Return the Point at a specified U and V parameters.
Surface.Thicken Thicken Surface into a Solid, extruding in the direction of Surface normals on both sides of the Surface.
CREATE
UV.ByCoordinates Create a UV from two doubles.
CREATE
Vector.ByCoordinates Form a Vector by 3 Euclidean coordinates
Vector.XAxis Gets the canonical X axis Vector (1,0,0)
Vector.YAxis Gets the canonical Y axis Vector (0,1,0)
Vector.ZAxis Gets the canonical Z axis Vector (0,0,1)
ACTIONS
Vector.Normalized Get the normalized version of a vector
CREATE
CoordinateSystem.ByOrigin Create a CoordinateSystem with origin at input Point, with X and Y Axes set as WCS X and Y axes
CoordinateSystem.ByCylindricalCoordinates Creates a CoordinateSystem at the specified cylindrical coordinate parameters with respect to the specified coordinate system
+ Addition
- Subtraction
* Multiplication
/ Division
% Modular Division finds the remainder of the first input after dividing by the second input
< Less Than
> Greater Than
== Equality tests for equality between two values.
Dynamo 2.0 exposes a variety of Dictionary nodes for our use. This includes create, action, and query nodes.
1.Dictionary.ByKeysValues
will create a dictionary with the supplied values and keys. (The number of entries will be whatever the shortest list input is)
2. Dictionary.Components
will produce the components of the input dictionary. (This is the reverse of the create node.)
3. Dictionary.RemoveKeys
will produce a new dictionary object with the input keys removed.
4. Dictionary.SetValueAtKeys
will produce a new dictionary based on the input keys and the values to replace the current value at the corresponding keys.
5. Dictionary.ValueAtKey
will return the value at the input key.
6. Dictionary.Count
will tell you how many key value pairs are in the dictionary.
7. Dictionary.Keys
will return what keys are currently stored in the dictionary.
8. Dictionary.Values
will return what values are currently stored in the dictionary.
Overall relating data with dictionaries is a magnificent alternative to the old method of working with indices and lists.
Dynamo 2.0 introduces the concept of separating the dictionary data type from the list data type. This change can pose some significant changes to how you create and work with data in your workflows. Prior to 2.0, dictionaries and lists were combined as a data type. In short, lists were actually dictionaries with integer keys.
A dictionary is a data type composed of a collection of key-value pairs where each key is unique in each collection. A dictionary has no order and basically you can “look things up” using a key instead of an index value like in a list. In Dynamo 2.0, keys can only be strings.
A list is a data type composed of a collection of ordered values. In Dynamo, lists use integers as index values.
The separation of dictionaries from lists introduces dictionaries as a first-class citizen that you can use to quickly and easily store and lookup values without needing to remember an index value or maintain a strict list structure throughout your workflow. During user testing, we saw a significant reduction in graph size when dictionaries were utilized instead of several GetItemAtIndex
nodes.
Syntax changes have occurred that change how you will initialize and work with dictionaries and lists in code blocks.
Dictionaries use the following syntax {key:value}
Lists use the following syntax [value,value,value]
New nodes have been introduced to the library to help you create, modify, and query dictionaries.
Lists created in v1.x code blocks will automatically be migrated on load of the script to the new list syntax that uses square brackets [ ]
instead of curly brackets { }
\
In computer science, Dictionaries - like lists- are collections of objects. While lists are in a specific order, dictionaries are unordered collections. They are not reliant on sequential numbers (indices), instead, they utilize keys.
In the image below we demonstrate a potential use case of a dictionary. Often times dictionaries are used to relate two pieces of data that might not have a direct correlation. In our case, we are connecting the Spanish version of a word to the English version for later lookup.
Build a dictionary to relate the two pieces of data.
Get the value with the given key.
Further down the rabbit-hole, let's add even more tiers to hierarchy. Data structure can expand far beyond a two-dimensional list of lists. Since lists are items in and of themselves in Dynamo, we can create data with as many dimensions as possible.
The analogy we'll work with here are Russian Nesting Dolls. Each list can be regarded as one container holding multiple items. Each list has its own properties and is also regarded as its own object.
A set of Russian Nesting Dolls (Photo by Zeta) is an analogy for n-Dimensional lists. Each layer represents a list, and each list contains items within it. In Dynamo's case, each container can have multiple containers inside (representing the items of each list).
n-Dimensional lists are difficult to explain visually, but we've set up a few exercises in this chapter which focus on working with lists which venture beyond two dimensions.
Mapping is arguably the most complex part of data management in Dynamo, and is especially relevant when working with complex hierarchies of lists. With the series of exercises below, we'll demonstrate when to use mapping and combinations as data becomes multi-dimensional.
Preliminary introductions to List.Map and List.Combine can be found in the previous section. In the last exercise below, we'll use these nodes on a complex data structure.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This exercise is the first in a series of three which focuses on articulating imported geometry. Each part in this series of exercises will increase in the complexity of data structure.
Let's begin with the .sat file in the exercise file folder. We can grab this file using the File Path node.
With Geometry.ImportFromSAT, the geometry is imported into our Dynamo preview as two surfaces.
For this exercise, we want to keep it simple and work with one of the surfaces.
Let's select the index of 1 to grab the upper surface. We do this with List.GetItemAtIndex node.
Switch off the geometry preview from Geometry.ImportFromSAT preview.
The next step is to divide the surface into a grid of points.
1. Using Code Block, insert these two lines of code:
0..1..#10;
0..1..#5;
2. With the Surface.PointAtParameter, connect the two code block values to u and v. Change the lacing of this node to "Cross Product".
3. The output reveals the data structure, which is also visible in the Dynamo preview.
Next, used the Points from last step to generate ten curves along the surface.
To get a look at how the data structure is organized, let's connect a NurbsCurve.ByPoints to the output of Surface.PointAtParameter.
You may switch off the preview from the List.GetItemAtIndex Node for now for a clearer result.
A basic List.Transpose will flip the columns and rows of a list of lists.
Connecting the output of List.Transpose to NurbsCurve.ByPoints, we now get five curves running horizontally across the surface.
You may switch off the preview from the NurbsCurve.ByPoints Node in the previous step to achieve the same result in the image.
Let's increase the complexity. Suppose we wanted to perform an operation on the curves created from the previous exercise. Perhaps we want to relate these curves to another surface and loft between them. This requires more attention to data structure, but the underlying logic is the same.
Begin with a step from the previous exercise, isolating the upper surface of the imported geometry with the List.GetItemAtIndex node.
Using Surface.Offset, offset the surface by a value of 10.
In the same manner as the previous exercise, define a code block with these two lines of code:
0..1..#10;
0..1..#5;
Connect these outputs to two Surface.PointAtParameter nodes, each with lacing set to "Cross Product". One of these nodes is connected to the original surface, while the other is connected to the offset surface.
Switch off the preview of these Surfaces.
As in the previous exercise, connect the outputs to two NurbsCurve.ByPoints nodes. The result show curves corresponding to two surfaces.
By using List.Create, we can combine the two sets of curves into one list of lists.
Notice from the output, we have two lists with ten items each, representing each connect set of Nurbs curves.
By performing a Surface.ByLoft, we can visually make sense of this data structure. The node lofts all of the curves in each sublist.
Switch off the preview from Surface.ByLoft Node in previous step.
By using List.Transpose, remember, we are flipping all of the columns and rows. This node will transfer two lists of ten curves into ten lists of two curves. We now have each nurbs curve related to the neighboring curve on the other surface.
Using Surface.ByLoft, we arrive at a ribbed structure.
Next, we will demonstrate an alternative process to achieve this result
Before we start, switch off the Surface.ByLoft preview in previous step to avoid confusion.
An alternative to List.Transpose uses List.Combine. This will operate a "combinator" on each sublist.
In this case, we're using List.Create as the "combinator", which will create a list of each item in the sublists.
Using the Surface.ByLoft node, we get the same surfaces as in the previous step. Transpose is easier to use in this case, but when the data structure becomes even more complex, List.Combine is more reliable.
Stepping back a few steps, if we want to switch the orientation of the curves in the ribbed structure, we want to use a List.Transpose before connect to NurbsCurve.ByPoints. This will flip the columns and rows, giving us 5 horizontal ribs.
Now, we're going to go even one step further. In this exercise, we'll work with both imported surfaces, creating a complex data hierarchy. Still, our aim is to complete the same operation with the same underlying logic.
Begin with the imported file from previous exercise.
As in the previous exercise, use the Surface.Offset node to offset by a value of 10.
Notice from the output, that we've created two surfaces with the offset node.
In the same manner as the previous exercise, define a Code Block with these two lines of code:
0..1..#20;
0..1..#20;
Connect these outputs to two Surface.PointAtParameter nodes, each with lacing set to "Cross Product". One of these nodes is connected to the original surfaces, while the other is connected to the offset surfaces.
As in the previous exercise, connect the outputs to two NurbsCurve.ByPoints nodes.
Looking at the output of the NurbsCurve.ByPoints, notice that this is a list of two lists, which is more complex than the previous exercise. The data is categorized by the underlying surface, so we've added another tier to the data structured.
Notice that things become more complex in the Surface.PointAtParameter node. In this case we have a list of lists of lists.
Before we proceed, switch off the preview from the existing surfaces.
Using the List.Create node, we merge the Nurbs curves into one data structure, creating a list of lists of lists.
By connecting a Surface.ByLoft node, we get a version of the original surfaces, as they each remain in their own list as created from the original data structure.
In the previous exercise, we were able to use a List.Transpose to create a ribbed structure. This won't work here. A transpose should be used on a two-dimensional list, and since we have a three-dimensional list, an operation of "flipping columns and rows" won't work as easily. Remember, lists are objects, so List.Transpose will flip our lists with out sublists, but won't flip the nurbs curves one list further down in the hierarchy.
List.Combine will work better for us here. We want to use List.Map and List.Combine nodes when we get to more complex data structures.
Using List.Create as the "combinator", we create a data structure that will work better for us.
The data structure still needs to be transposed at one step down on the hierarchy. To do this we'll use List.Map. This is working like List.Combine, except with one input list, rather than two or more.
The function we'll apply to List.Map is List.Transpose, which will flip the columns and rows of the sublists within our main list.
Finally, we can loft the Nurbs curves together with a proper data hierarchy, giving us a ribbed structure.
Let's add some depth to the geometry with a Surface.Thicken Node with the input settings as shown.
It'll be nice to add a surface backing two this structure, so add another Surface.ByLoft node and use the first output of NurbsCurve.ByPoints from an earlier step as input.
As the preview is getting cluttered, switch off preview for these nodes by right clicking on each of them and uncheck 'preview' to see the result better.
And thickening these selected surfaces, our articulation is complete.
Not the most comfortable rocking chair ever, but it's got a lot of data going on.
Last step, let's reverse the direction of the striated members. As we used transpose in the previous exercise, we'll do something similar here.
Since we have one more tier to the hierarchy, so we need to use List.Map with a List.Transpose function to change the direction of our Nurbs curves.
We may want to increase the number of treads, so we can change the Code Block to
0..1..#20;
0..1..#30;
The first version of the rocking chair was sleek, so our second model offers an off-road, sport-utility version of recumbency.
Dictionaries represent a collection of data that is related to another piece of data known as a key. Dictionaries expose the ability to search for, delete and insert data into a collection.
Essentially, we can think of a dictionary as a really smart way to look something up.
While dictionary functionality has been available in Dynamo for some time, Dynamo 2.0 introduces a new way of managing this data type.
Original image courtesy of: sixtysecondrevit.com
We've just created a custom node and applied it to a specific process in our Dynamo graph. And we like this node so much, we want to keep it in our Dynamo library to reference in other graphs. To do this, we'll publish the node locally. This is a similar process to publishing a package, which we'll walk through in more detail in the next chapter.
By publishing a node locally, the node will be accessible in your Dynamo library when you open a new session. Without publishing a node, a Dynamo graph which references a custom node must also have that custom node in its folder (or the custom node must be imported into Dynamo using File > Import Library).
You can publish custom nodes and packages from Dynamo Sandbox in version 2.17 and newer, as long as they have no host API dependencies. In older versions, publishing custom nodes and packages is only enabled in Dynamo for Revit and Dynamo for Civil 3D.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Let's move forward with the custom node that we created in the previous section. Once the PointsToSurface custom node is opened, we see the graph in the Dynamo Custom Node Editor. You can also open up a custom node by double clicking on it in the Dynamo Graph Editor.
To Publish a custom node locally, simply right click on the canvas and select "Publish This Custom Node..."
Fill out the relevant information similar to the image above and select "Publish Locally". Note that the Group field defines the main element accessible from the Dynamo menu.
Choose a folder to house all of the custom nodes that you plan on publishing locally. Dynamo will check this folder each time it loads, so make sure the folder is in a permanent place. Navigate to this folder and choose "Select Folder". Your Dynamo node is now published locally, and will remain in your Dynamo library each time you load the program!
To check on the custom node folder location, go to Dynamo > Preferences > Package Settings > Node and Package Paths.
In this window we see a list of paths.
Documents\DynamoCustomNodes... refers to the location of custom nodes we've published locally.
AppData\Roaming\Dynamo... refers to the default location of Dynamo Packages installed online.
You may want to move your local folder path down in the list order (by clicking on the down arrow to the left of the path name). The top folder is the default path for package installs. So by keeping the default Dynamo package install path as the default folder, online packages will be separated from your locally published nodes.
We switched the order of the path names in order to have Dynamo's default path as the package install location.
Navigating to this local folder, we can find the original custom node in the ".dyf" folder, which is the extension for a Dynamo Custom Node file. We can edit the file in this folder and the node will update in the UI. We can also add more nodes to the main DynamoCustomNode folder and Dynamo will add them to your library at restart!
Dynamo will now load each time with "PointsToSurface" in the "DynamoPrimer" group of your Dynamo library.
Dynamo offers many Core Nodes for a wide range of visual programming tasks. Sometimes a quicker, more elegant, or more easily shared solution is to build your own Nodes. These can be reused among different projects, they make your graphs clearer and cleaner, and they can be pushed to the package manager and shared with the global Dynamo community.
Out of the box, Dynamo has a lot of functionality stored in its Library of Nodes. For those frequently used routines or that special graph you want to share with the community, Custom Nodes & Packages are a great way to extend Dynamo even further.
Once you have created a few Custom Nodes the very next step is to begin organizing and publishing them by way of Packages - a convenient way to store and share your nodes with the Dynamo Community.
The Dynamo Mesh Toolkit provides tools to import meshes from external file formats, create a mesh from Dynamo geometry objects, and manually build meshes by their vertices and indices. The library also provides tools to modify meshes, repair meshes, or extract horizontal slices for use in fabrication.
The Dynamo Mesh Toolkit is part of Autodesk's ongoing mesh research, and as such will continue to grow over the coming years. Expect new methods to appear on the toolkit frequently, and feel free to reach out to the Dynamo team with comments, bugs, and suggestions for new features.
The exercise below demonstrates some basic mesh operations using the Mesh Toolkit. In the exercise, we intersect a mesh with a series of planes, which can be computationally expensive using solids. Unlike a solid, a mesh has a set "resolution" and is not defined mathematically, but topologically, and we can define this resolution based on the task at hand. For more details on mesh to solid relationships, you can reference the Geometry For Computation Design chapter in this primer. For a more thorough examination of Mesh Toolkit, you can reference the Dynamo Wiki page. Let's jump into the package in the exercise below.
In Dynamo, go to Packages > Package Manager... in the top menu bar. In the search field, type MeshToolkit, all one word. Click Install and accept the confirmations to start the download. Simple as that!
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this example, we will look at the Intersect node in the mesh toolkit. We will import a mesh and intersect it with a series of input planes to create slices. This is the starting point for preparing the model for fabrication on a laser cutter, waterjet cutter, or CNC mill.
Begin by opening Mesh-Toolkit_Intersect-Mesh.dyn in Dynamo.
File Path: Locate the mesh file to import (stanford_bunny_tri.obj). Supported file types are .mix and .obj
Mesh.ImportFile: Connect the file path to import the mesh
Point.ByCoordinates: Construct a point – this will be the center of an arc.
Arc.ByCenterPointRadiusAngle: Construct an arc around the point. This curve will be used to position a series of planes. __ The settings are as follow: __
radius: 40, startAngle: -90, endAngle:0
Create a series of planes oriented along the arc.
Code Block: Create 25 numbers between 0 and 1.
Curve.PointAtParameter: Connect the arc to the ‘curve’ input and the code block output to the ‘param’ input to extract a series of points along the curve.
Curve.TangentAtParameter: Connect the same inputs as the previous node.
Plane.ByOriginNormal: Connect the points to the ‘origin’ input and the vectors to the ‘normal’ input to create a series of planes at each point.
Next, we will use these planes to intersect the mesh.
Mesh.Intersect: Intersect the planes with the imported mesh, creating a series of polycurve contours. Right click on Node and set the lacing to longest
PolyCurve.Curves: Break the polycurves into their curve fragments.
Curve.EndPoint: Extract the end points of each curve.
NurbsCurve.ByPoints: Use the points to construct a nurbs curve. Use a Boolean node set to True to close the curves.
Before we continue, switch off the preview for some of the Nodes such as: Mesh.ImportFile, Curve.EndPoint, Plane.ByOriginNormal & Arc.ByCenterPointRadiusAngle to see the result better.
Surface.ByPatch: Construct surface patches for each contour to create “slices” of the mesh.
Add a second set of slices for a waffle/egg-crate effect.
You may have noticed that the intersection operations calculate faster with a mesh vs. a comparable solid. Workflows such as the one demonstrated in this exercise lend themselves well to working with meshes.
While Dynamo is a flexible environment, designed to port into a wide range of programs, it was originally created for use with Revit. A visual program creates robust options for a Building Information Model (BIM). Dynamo offers a whole suite of nodes specifically designed for Revit, as well as third-party libraries from a thriving AEC community. This chapter focuses on the basics of using Dynamo in Revit.
Zero-Touch Importing refers to a simple point-and-click method for importing C# libraries. Dynamo will read the public methods of a .dll file and convert them to Dynamo nodes. You can use Zero-Touch to develop your own custom nodes and packages, and to import external libraries into the Dynamo environment.
.dll files
Dynamo Nodes
With Zero-Touch, you can actually import a library which was not necessarily developed for Dynamo and create a suite of new nodes. The current Zero-Touch functionality demonstrates the cross-platform mentality of the Dynamo Project.
This section demonstrates how to use Zero-Touch to import a third party library. For information on developing your own Zero-Touch Library, reference the Dynamo wiki page.
Zero-touch packages are a good complement to user-defined custom nodes. A few packages which use C# libraries are listed in the table below. For more detailed information on packages, visit the Packages section in the Appendix.
Logo/Image
Name
In this case study, we'll show how to import the AForge external .dll library. AForge is a robust library which offers a range of functionality from image processing to artificial intelligence. We'll reference the imaging class in AForge to do a few image processing exercises below.
Let's begin by downloading AForge. On the AForge download page, select [Download Installer] and install after download has completed.
In Dynamo, create a new file and select File > Import Library...
Next, locate the dll file.
In the pop-up window, navigate to the release folder in your AForge install. This will likely be in a folder similar to this one: C:\Program Files (x86)\AForge.NET\Framework\Release.
AForge.Imaging.dll: We only want to use this one file from the AForge library for this case study. Select this .dll and hit "Open".
Back in Dynamo, you should see an AForge group of nodes added to your Library. We now have access to the AForge imaging library from our visual program!
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Now that the library is imported, we'll start off simple with this first exercise (01-EdgeDetection.dyn). We'll do some basic image processing on a sample image to show how AForge image filters. We'll use the "Watch Image" node to show our results and apply filters in Dynamo similar to those in Photoshop
To import an image, add a File Path node to the canvas and select "soapbubbles.jpg" from the exercise folder (photo cred: flickr).
The File Path node simply provides a String of the path to the image we've selected. Next, we need to convert it into a usable image file in Dynamo.
Use File From Path to convert the file path item into an image in the Dynamo environment.
Connect the File Path node to the File.FromPath node.
To convert this File into an Image, we'll use the Image.ReadFromFile node.
Last, let's see the result! Drop a Watch Image node onto the canvas and connect to Image.ReadFromFile. We haven't used AForge yet, but we've successfully imported an image into Dynamo.
Under AForge.Imaging.AForge.Imaging.Filters (in the navigation menu), you'll notice that there is a wide array of filters available. We're going to use one of these filters now to desaturate an image based on threshold values.
Drop three sliders onto the canvas, change their ranges to be from 0 to 1 and their step values to be 0.01.
Add the Grayscale.Grayscale node to the canvas. This is an AForge filter which applies a grayscale filter to an image. Connect the three sliders from step 1 into cr, cg, and cb. Change the top and bottom sliders to have a value of 1 and the middle slider to have a value of 0.
In order to apply the Grayscale filter, we need an action to perform on our image. For this, we use BaseFilter.Apply. Connect the image into the image input and Grayscale.Grayscale into the baseFilter input.
Plugging into a Watch Image node, we get a desaturated image.
We can have control over how to desaturate this image based on threshold values for red, green, and blue. These are defined by the inputs to the Grayscale.Grayscale node. Notice that the image looks pretty dim - this is because the green value is set to 0 from our slider.
Change the top and bottom sliders to have a value of 0 and the middle slider to have a value of 1. This way we get a more legible desaturated image.
Let's use the desaturated image, and apply another filter on top of it. The desaturated image has some contrast, so we we're going to test some edge detection.
Add a SobelEdgeDetector.SobelEdgeDetector node to the canvas.
Connect this to a BaseUsingCopyPartialFilter.Apply and connect the desaturated image to the image input of this node.
The Sobel Edge Detector has highlighted the edges in a new image.
Zooming in, the edge detector has called out the outlines of the bubbles with pixels. The AForge library has tools to take results like this and create Dynamo geometry. We'll explore that in the next exercise.
Now that we're introduced to some basic image processing, let's use an image to drive Dynamo geometry! On an elementary level, in this exercise we're aiming to do a "Live Trace" of an image using AForge and Dynamo. We're going to keep it simple and extract rectangles from a reference image, but there are tools available in AForge for more complex operations. We'll be working with 02-RectangleCreation.dyn from the downloaded exercise files.
With the File Path node, navigate to grid.jpg in the exercise folder.
Connect the remaining series of nodes above to reveal a course parametric grid.
In this next step, we want to reference the white squares in the image and convert them to actual Dynamo geometry. AForge has a lot of powerful Computer Vision tools, and here we're going to use a particularly important one for the library called BlobCounter.
Add a BlobCounter to the canvas, then we need a way to process the image (similar to the BaseFilter.Apply tool in the previous exercise).
Unfortunately the "Process Image" node is not immediately visible in the Dynamo library. This is because the function may not be visible in the AForge source code. In order to fix this, we'll need to find a work-around.
Add a Python node to the canvas and add the following code to the Python node. This code imports the AForge library and then processes the imported image.
Connecting the image output to the Python node input, we get an AForge.Imaging.BlobCounter result from the Python node.
The next steps will do some tricks that demonstrate familiarity with the AForge Imaging API. It's not necessary to learn all of this for Dynamo work. This is more of a demonstration of working with external libraries within the flexibility of the Dynamo environment.
Connect the output of the Python script to BlobCounterBase.GetObjectRectangles. This reads objects in an image, based on a threshold value, and extracts quantified rectangles from the pixel space.
Adding another Python node to the canvas, connect to the GetObjectRectangles, and input the code below. This will create an organized list of Dynamo objects.
Transpose the output of the Python node from the previous step. This creates 4 lists, each representing X,Y, Width, and Height for each rectangle.
Using Code Block, we organize the data into a structure that accommodates the Rectangle.ByCornerPoints node (code below).
We have an array of rectangles representing the white squares in the image. Through programming, we've done something (roughly) similar to a live trace in Illustrator!
We still need some cleanup, however. Zooming in, we can see that we have a bunch of small, unwanted rectangles.
Next, we are going to write codes to get rid of unwanted rectangles.
Insert a Python node in between the GetObjectRectangles node and another Python node. The node's code is below, and removes all rectangles which are below a given size.
With the superfluous rectangles gone, just for kicks, let's create a surface from these rectangles and extrude them by a distance based on their areas.
Last, change the both_sides input to false and we get an extrusion in one direction. Dip this baby in resin and you've got yourself one super nerdy table.
These are basic examples, but the concepts outlined here are transferable to exciting real-world applications. Computer vision can be used for a whole host of processes. To name a few: barcode readers, perspective matching, projection mapping, and augmented reality. For more advanced topics with AForge related to this exercise, have a read through this article.
In the previous sections, we dove into the details of how our MapToSurface package is set up with custom nodes and example files. But how do we publish a package that has been developed locally? This case study demonstrates how to publish a package from a set of files in a local folder.
There are many ways to publish a package. Below is the process that we advise: publish locally, develop locally, and then publish online. We'll start with a folder containing all of the files in the package.
Before we jump into publishing the MapToSurface package, if you installed the package from the previous lesson, uninstall it so that you're not working with identical packages.
Begin by going to Packages > Package Manager > Installed Packages tab > next to MapToSurface click the vertical dots menu > Delete.
Then restart Dynamo. When reopening, when you check the "Manage Packages" window, the MapToSurface should no longer be there. Now we're ready to start from the beginning!
You can publish custom nodes and packages from Dynamo Sandbox in version 2.17 and newer, as long as they have no host API dependencies. In older versions, publishing custom nodes and packages is only enabled in Dynamo for Revit and Dynamo for Civil 3D.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This is the first submission for our package, and we've placed all of the example files and custom nodes into one folder. With this folder prepared, we're ready to upload to the Dynamo Package Manager.
This folder contains five custom nodes (.dyf).
This folder also contains five example files (.dyn) and one imported vector file (.svg). These files will serve as introductory exercises to show the user how to work with the custom nodes.
In Dynamo, begin by clicking Packages > Package Manager > Publish New Package tab.
In the Publish a Package tab, fill out the relevant fields on the left side of the window.
Next, we’ll add package files. You can add files one by one or entire folders by selecting Add Directory (1). To add files that are not .dyf files, be sure to change your file type in the browser window to "All Files(.)". Notice that we’ll be adding every file, custom node (.dyf) or example file (.dyn), indiscriminately. Dynamo will categorize these items when we publish the package.
Once you’ve selected the MapToSurface folder, Package Manager shows you the folder contents. If you are uploading your own package with a complex folder structure, and you do not want Dynamo to make changes to your folder structure, you can enable the “Retain folder structure” toggle. This option is for advanced users, and if your package isn’t deliberately set up in a specific way, it’s best to leave this toggle off and allow Dynamo to organize the files as needed. Click Next to proceed.
Here, you have a chance to preview how Dynamo will organize your package files prior to publishing. Click Finish to proceed.
Publish by clicking "Publish Locally" (1). If you're following along, be certain to click "Publish Locally" and not "Publish Online” to avoid having a bunch of duplicate packages in the Package Manager.
After publishing, the custom nodes should be available under the "DynamoPrimer" group or your Dynamo Library.
Now let's look at the root directory to see how Dynamo has formatted the package we just created. Do this by going to the Installed Packages tab > next to MapToSurface, click on the vertical dots menu > select Show Root Directory.
Notice that the root directory is in the local location of your package (remember, we published the package "locally"). Dynamo is currently referencing this folder to read custom nodes. It's therefore important to locally publish the directory to a permanent folder location (i.e., not your desktop). Here is the Dynamo package folder breakdown.
The bin folder houses .dll files created with C# or Zero-Touch libraries. We don't have any for this package so this folder is blank for this example.
The dyf folder houses the custom nodes. Opening this will reveal all of the custom nodes (.dyf files) for this package.
The extra folder houses all additional files. These files are likely to be Dynamo Files (.dyn) or any additional files required (.svg, .xls, .jpeg, .sat, etc.).
The pkg file is a basic text file defining the package settings. This is automated in Dynamo, but can be edited if you want to get into the details.
Note: please do not follow along with this step unless you are actually publishing a package of your own!
When you're ready to publish, in the Packages > Package Manager > Installed Packages window, select the button the right of the package you want to publish and choose Publish.
If you're updating a package that has already been published, choose "Publish Version" and Dynamo will update your package online based on the new files in that package's root directory. Simple as that!
When you update the files in your published package's root folder, you can also publish a new version of the package by selecting "Publish Version..." in the My Packages tab. This is a seamless way to make necessary updates to your content and share with the community. Publish Version will only work if you're the maintainer of the package.
Currently, you cannot transfer package ownership via the Package Manager. You can ask the Dynamo team to add an additional owner. Note we cannot remove existing owners, only add more maintainers of the package. If you wish to add an account as an owner to your existing package, please send an email to dynamoteam@dynamobim.org. Make sure to provide the package name and the account name you wish to add.
You can create an array of Revit elements in Dynamo with full parametric control. The Revit nodes in Dynamo offer the ability to import elements from generic geometries to specific category types (like walls and floors). In this section, we'll focus on importing parametrically flexible elements with adaptive components.
An adaptive component is a flexible family category which lends itself well to generative applications. Upon instantiation, you can create a complex geometric element which is driven by the fundamental location of adaptive points.
Below is an example of a three-point adaptive component in the family editor. This generates a truss which is defined by the position of each adaptive point. In the exercise below, we'll use this component to generate a series of trusses across a facade.
The adaptive component is a good example for best practices of interoperability. We can create an array of adaptive components by defining the fundamental adaptive points. And, when transferring this data to other programs, we have the ability to reduce the geometry to simple data. Importing and exporting with a program like Excel follows a similar logic.
Suppose a facade consultant wants to know the location of the truss elements without needing to parse through fully articulated geometry. In preparation for fabrication, the consultant can reference the location of adaptive points to regenerate geometry in a program like Inventor.
The workflow we'll setup in the exercise below allows us to access all of this data while creating the definition for Revit element creation. By this process, we can merge conceptualization, documentation, and fabrication into a seamless workflow. This creates a more intelligent and efficient process for interoperability.
The first exercise below will walk through how Dynamo references data for Revit element creation. To generate multiple adaptive components, we define a list of lists, where each list has three points representing each point of the adaptive component. We'll keep this in mind as we manage the data structures in Dynamo.
Another method for importing parametric Dynamo geometry into Revit is with DirectShape. In summary, the DirectShape element and related classes support the ability to store externally created geometric shapes in a Revit document. The geometry can include closed solids or meshes. DirectShape is primarily intended for importing shapes from other data formats such as IFC or STEP where not enough information is available to create a "real" Revit element. Like the IFC and STEP workflow, the DirectShape functionality works well with importing Dynamo created geometries into Revit projects as real elements.
Let's walk through second exercise for importing Dynamo geometry as a DirectShape into our Revit project. Using this method, we can assign an imported geometry's category, material, and name - all while maintaining a parametric link to our Dynamo graph.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Beginning with the example file from this section (or continuing with the Revit file from the previous session), we see the same Revit mass.
This is the file as opened.
This is the truss system we created with Dynamo, linked intelligently to the Revit mass.
We've used the "Select Model Element" and "Select Face" nodes, now we're taking one step further down in the geometry hierarchy and using "Select Edge". With the Dynamo solver set to run "Automatic", the graph will continually update to changes in the Revit file. The edge we are selecting is tied dynamically to the Revit element topology. As long as the topology* does not change, the connection remains linked between Revit and Dynamo.
Select the top most curve of the glazing facade. This spans the full length of the building. If you're having trouble selecting the edge, remember to choose the selection in Revit by hovering over the edge and hitting "Tab" until the desired edge is highlighted.
Using two "Select Edge" nodes, select each edge representing the cant at the middle of the facade.
Do the same for the bottom edges of the facade in Revit.
The Watch nodes reveal that we now have lines in Dynamo. This is automatically converted to Dynamo geometry since the edges themselves are not Revit elements. These curves are the references we'll use to instantiate adaptive trusses across the facade.
*To keep a consistent topology, we're referring to a model that does not have additional faces or edges added. While parameters can change its shape, the way in which it is built remains consistent.
We first need to join the curves and merge them into one list. This way we can "group" the curves to perform geometry operations.
Create a list for the two curves at the middle of the facade.
Join the two curves into a Polycurve by plugging the List.Create component into a Polycurve.ByJoinedCurves node.
Create a list for the two curves at the bottom of the facade.
Join the two curves into a Polycurve by plugging the List.Create component into a Polycurve.ByJoinedCurves node.
Finally, join the three main curves (one line and two polycurves) into one list.
We want to take advantage of the top curve, which is a line, and represents the full span of the facade. We'll create planes along this line to intersect with the set of curves we've grouped together in a list.
With a code block, define a range using the syntax:
0..1..#numberOfTrusses;
Plug an *integer slider *into the input for the code block. As you could have guessed, this will represent the number of trusses. Notice that the slider controls the number of items in the range defined from *0 *to 1.
Plug the code block into the param input of a "Curve.PlaneAtParameter" node, and plug the top edge into the curve input. This will give us ten planes, evenly distributed across the span of the facade.
A plane is an abstract piece of geometry, representing a two dimensional space which is infinite. Planes are great for contouring and intersecting, as we are setting up in this step.
Using the Geometry.Intersect node (set lacing option to cross product), plug the Curve.PlaneAtParameter into the entity input of the Geometry.Intersect node. Plug the main List.Create node into the geometry input. We now see points in the Dynamo viewport representing the intersection of each curve with the defined planes.
Notice the output is a list of lists of lists. Too many lists for our purposes. We want to do a partial flatten here. We need to take one step down on the list and flatten the result. To do this, we use the List.Map operation, as discussed in the list chapter of the primer.
Plug the Geometry.Intersect node into the list input of List.Map.
Plug a Flatten node into the f(x) input of List.Map. The results gives 3 list, each with a count equal to the number of trusses.
We need to change this data. If we want to instantiate the truss, we have to use the same number of adaptive points as defined in the family. This is a three point adaptive component, so instead of three lists with 10 items each (numberOfTrusses), we want 10 lists of three items each. This way we can create 10 adaptive components.
Plug the List.Map into a List.Transpose node. Now we have the desired data output.
To confirm that the data is correct, add a Polygon.ByPoints node to the canvas and double check with the Dynamo preview.
In the same way we created the polygons, we array the adaptive components.
Add an AdaptiveComponent.ByPoints node to the canvas, plug the List.Transpose node into the points input.
Using a Family Types node, select the "AdaptiveTruss" family, and plug this into the FamilyType input of the AdaptiveComponent.ByPoints node.
In Revit, we now have the ten trusses evenly spaced across the facade!
"Flex" the graph, we turn up the numberOfTrusses to 30 by changing the slider. Lots of trusses, not very realistic, but the parametric link is working. Once verified, set the numberOfTrusses to 15.
And for the final test, by selecting the mass in Revit and editing instance parameters, we can change the form of the building and watch the truss follow suit. Remember, this Dynamo graph has to be open in order to see this update, and the link will be broken as soon as it's closed.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Begin by opening the sample file for this lesson - ARCH-DirectShape-BaseFile.rvt.
In the 3D view, we see our building mass from the previous lesson.
Along the edge of the atrium is one reference curve, we'll use this as a curve to reference in Dynamo.
Along the opposing edge of the atrium is another reference curve which we'll reference in Dynamo as well.
To reference our geometry in Dynamo, we'll use Select Model Element for each member in Revit. Select the mass in Revit and import the geometry into Dynamo by Using Element.Faces - the mass should now be visible in your Dynamo preview.
Import one reference curve into Dynamo by using Select Model Element and CurveElement.Curve.
Import the other reference curve into Dynamo by using Select Model Element and CurveElement.Curve.
Zooming out and panning to the right in the sample graph, we see a large group of nodes - these are geometric operations which generate the trellis roof structure visible in the Dynamo preview. These nodes are generating using the Node to Code functionality as discussed in the code block section of the primer.
The structure is driven by three major parameters - Diagonal Shift, Camber, and Radius.
Zooming a close-up look of the parameters for this graph. We can flex these to get different geometry outputs.
Dropping the DirectShape.ByGeometry node onto the canvas, we see that it has four inputs: geometry, category, material, and name.
Geometry will be the solid created from the geometry creation portion of the graph
The category input is chosen using the dropdown Categories node. In this case we'll use "Structural Framing".
The material input is selected through the array of nodes above - although it can be more simply defined as "Default" in this case.
After running Dynamo, back in Revit, we have the imported geometry on the roof in our project. This is a structural framing element, rather than a generic model. The parametric link to Dynamo remains intact.
While we previously looked at editing a basic building mass, we want to dive deeper into the Dynamo/Revit link by editing a large number of elements in one go. Customizing on a large scale becomes more complex as data structures require more advanced list operations. However, the underlying principles behind their execution is fundamentally the same. Let's study some opportunities for analysis from a set of adaptive components.
Suppose we've created a range of adaptive components and want to edit parameters based on their point locations. The points, for example, could drive a thickness parameter which is related to the area of the element. Or, they could drive an opacity parameter related to solar exposure throughout the year. Dynamo allows the connection of analysis to parameters in a few easy steps, and we'll explore a basic version in the exercise below.
Query the adaptive points of a selected adaptive component by using the AdaptiveComponent.Locations node. This allows us to work with an abstracted version of a Revit element for analysis.
By extracting the point location of adaptive components, we can run a range of analysis for that element. A four-point adaptive component will allow you to study the deviation from plane for a given panel for example.
Use remapping to map a set of a data into a parameter range. This is fundamental tool used in a parametric model, and we'll demonstrate it in the exercise below.
Using Dynamo, the point locations of adaptive components can be used to create a best-fit plane each element. We can also query the sun position in the Revit file and study the plane's relative orientation to the sun in comparison to other adaptive components. Let's set that up in the exercise below by creating an algorithmic roofscape.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This exercise will expand on the techniques demonstrated in the previous section. In this case, we are defining a parametric surface from Revit elements, instantiating four-point adaptive components and then editing them based on orientation to the sun.
Beginning by selecting two edges with the "Select Edge" node. The two edges are the long spans of the atrium.
Combine the two edges into one list with the List.Create node.
Create a surface between the two edges with a Surface.ByLoft.
Using code block, define a range from 0 to 1 with 10 evenly spaced values:
0..1..#10;
Plug the code block into the *u *and v inputs of a Surface.PointAtParameter node, and plug the Surface.ByLoft node into the surface input. Right click the node and change the lacing to Cross Product. This will give a grid of points on the surface.
This grid of points serves as the control points for a parametrically defined surface. We want to extract the u and v positions of each one of these points so that we can plug them into a parametric formula and keep the same data structure. We can do this by querying the parameter locations of the points we just created.
Add a Surface.ParameterAtPoint node to the canvas, connect the inputs as shown above.
Query the u values of these parameters with the UV.U node.
Query the v values of these parameters with the UV.V node.
The outputs show the corresponding u and v values for every point of the surface. We now have a range from 0 to 1 for each value, in the proper data structure, so we're ready to apply a parametric algorithm.
Add a code block to the canvas and enter the code:
Math.Sin(u*180)*Math.Sin(v*180)*w;
This is a parametric function which creates a sine mound from a flat surface.Connects the UV.U to the u input and the UV.V to the v input.
The w input represents the amplitude of the shape, so we attach a number slider to it.
Now, we have a list of values as defined by the algorithm. Let's use this list of values to move the points up in the +Z direction. Using Geometry.Translate, plug the *code block *into zTranslation and the Surface.PointAtParameter into the geometry input. You should see the new points displayed in the Dynamo preview.
Finally, we create a surface with the NurbsSurface.ByPoints node, plugging the node from the previous step into the points input. We have ourselves a parametric surface. Feel free to drag the slider to watch the mound shrink and grow.
With the parametric surface, we want to define a way to panelize it in order to array four-point adaptive components. Dynamo does not have out-of-the-box functionality for surface panelization, so we can look to the community for helpful Dynamo packages.
Go to Packages>Search for a Package...
Search for "LunchBox" and install "LunchBox for Dynamo". This is a really helpful set of tools for geometry operations such as this.
After downloading, you now have full access to the LunchBox suite. Search for "Quad Grid" and select "LunchBox Quad Grid By Face". Plug the parametric surface into the surface input and set the U and V divisions to 15. You should see a quad-paneled surface in your Dynamo preview.
If you're curious about its setup, you can double click on the Lunch Box node and see how it's made.
Back in Revit, let's take a quick look at the adaptive component we're using here. No need to follow along, but this is the roof panel we're going to instantiate. It is a four-point adaptive component which is a crude representation of an ETFE system. The aperture of the center void is on a parameter called "ApertureRatio".
We're about to instantiate a lot of geometry in Revit, so make sure to turn the Dynamo solver to "Manual".
Add a Family Types node to the canvas and select "ROOF-PANEL-4PT".
Add an AdaptiveComponent.ByPoints node to the canvas, connect Panel Pts from the "LunchBox Quad Grid by Face" output into the points input. Connect the Family Types node to the familySymbol input.
Hit Run. Revit will have to think for a bit while the geometry is being created. If it takes too long, reduce the code block's '15' to a lower number. This will reduce the number of panels on the roof.
Note: If Dynamo is taking a long time to calculate nodes, you may want to use the "freeze" node functionality in order to pause the execution of Revit operations while you develop your graph. For more information on freezing nodes, check out the "Freezing" section in the solids chapter.
Back in Revit, we have the array of panels on the roof.
Zooming in, we can get a closer look at their surface qualities.
Continuing from the previous step, let's go further and drive the aperture of each panel based on its exposure to the sun. Zooming into Revit and select one panel, we see in the properties bar that there is a parameter called "Aperture Ratio". The family is setup so that the aperture ranges, roughly, from 0.05 to 0.45.
If we turn on the solar path, we can see the current sun location in Revit.
We can reference this sun location using the SunSettings.Current node.
Plug the Sun settings into Sunsetting.SunDirection to get the solar vector.
From the Panel Pts used to create the adaptive components, use Plane.ByBestFitThroughPoints to approximate a plane for the component.
Query the normal of this plane.
Use the dot product to calculate solar orientation. The dot product is a formula which determines how parallel or anti-parallel two vectors may be. So we're taking the plane normal of each adaptive component and comparing it to the solar vector to roughly simulate solar orientation.
Take the absolute value of the result. This ensures that the dot product is accurate if the plane normal is facing the reverse direction.
Hit Run.
Looking at the dot product, we have a wide range of numbers. We want to use their relative distribution, but we need to condense the numbers into the appropriate range of the "Aperture Ratio" parameter we plan to edit.
The Math.RemapRange is a great tool for this. It takes an input list and remaps its bounds into two target values.
Define the target values as 0.15 and 0.45 in a code block.
Hit Run.
Connect the remapped values into a Element.SetParameterByName node.
Connect the string "Aperture Ratio" into the parameterName input.
Connect the adaptive components into the element input.
Hit Run.
Back in Revit, from a distance we can make out the affect of the solar orientation on the aperture of the ETFE panels.
Zooming in, we see that the ETFE panels are more closed as the face the sun. Our target here is to reduce overheating from solar exposure. If we wanted to let in more light based on solar exposure, we just have to switch the domain on Math.RemapRange.
Dynamo for Civil 3D brings the visual programming paradigm to engineers and designers working on civil infrastructure projects. You can think of Dynamo as a sort of digital multi-tool for Civil 3D users - whatever the task, it has just the right tool for the job. Its intuitive interface enables you to create powerful and customizable routines without writing a single line of code. You don't need to be a programmer to use Dynamo, but you do need to be able to think with the logic of a programmer. Coupled with the other chapters in the Primer, this chapter will help you build your logic skills so that you can tackle any task with a computational design mindset.
Dynamo was first introduced in Civil 3D 2020 and has continued to evolve since that time. Initially installed separately via a software update, it now comes bundled with all versions of Civil 3D. Depending on which version of Civil 3D you are using, you may notice that the Dynamo interface looks slightly different than the examples you see in this chapter. This is because there was a significant overhaul to the interface in Civil 3D 2023.
It is recommended to take a look at the Dynamo Blog for the most up-to-date info regarding Dynamo's development. The table below summarizes the key milestones in the lifespan of Dynamo for Civil 3D.
2024.1
2.18
2024
2.17
Dynamo Player user interface update
2023.2
2.15
2023
2.13
Dynamo user interface update
2022.1
2.12
Added object binding data storage settings
New nodes for controlling object binding
2022
2.10
Included in main Civil 3D installation
Transition from IronPython to Python.NET
2021
2.5
2020.2
2.4
2020 Update 2
2.4
New nodes added
2020.1
2.2
2020
2.1
Initial release
Revit is a data-rich environment. This gives us a range of selection abilities which expands far beyond "point-and-click". We can query the Revit database and dynamically link Revit elements to Dynamo geometry while performing parametric operations.
The Revit library in the UI offers a "Selection" category which enables multiple ways to select geometry.
To select Revit elements properly, it's important to have a full-understanding of the Revit element hierarchy. Want to select all the walls in a project? Select by category. Want to select every Eames chair in your mid-century modern lobby? Select by family.
Let's do a quick review of the Revit hierarchy.
Remember the taxonomy from Biology? Kingdom, Phylum, Class, Order, Family, Genus, Species? Revit elements are categorized in a similar manner. On a basic level, the Revit hierarchy can be broken down into Categories, Families, Types*, and Instances. An instance is an individual model element (with a unique ID) while a category defines a generic group (like "walls" or "floors"). With the Revit database organized in this manner, we can select one element and choose all similar elements based on a specified level in the hierarchy.
*Types in Revit are defined differently from types in programming. In Revit, a type refers to a branch of the hierarchy, rather than a "data type".
The three images below breakdown the main categories for Revit element selection in Dynamo. These are great tools to use in combination, and we'll explore some of these in the following exercises.
Point-and-click is the easiest way to directly select a Revit element. You can select a full model element, or parts of its topology (like a face or an edge). This remains dynamically linked to that Revit object, so when the Revit file updates its location or parameters, the referenced Dynamo element will update in the graph.
Dropdown menus create a list of all accessible elements in a Revit project. You can use this to reference Revit elements which are not necessarily visible in a view. This is a great tool for querying existing elements or creating new ones in a Revit project or family editor.

You can also select Revit element by specific tiers in the Revit hierarchy. This is a powerful option for customizing large arrays of data in preparation for documentation or generative instantiation and customization.
With the three images above in mind, let's dive into an exercise which selects elements from a basic Revit project in preparation for the parametric applications we'll create in the remaining sections of this chapter.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this example Revit file, we have three element types of a simple building. We're going to use this as an example for selecting Revit elements within the context of the Revit hierarchy.
Building Mass
Beams (Structural Framing)
Trusses (Adaptive Components)
What conclusions can we draw from the elements currently in the Revit project view? And how far down the hierarchy do we need to go to select the appropriate elements? This will of course become a more complex task when working on a large project. There are a lot of options available: we can select elements by categories, levels, families, instances, etc.
Since we're working with a basic setup, let's select the building mass by choosing "Mass" in the Categories dropdown node. This can be found in the Revit>Selection tab.
The output of the Mass category is just the category itself. We need to select the elements. To do this, we use the "All Elements of Category" node.
At this point, notice that we don't see any geometry in Dynamo. We've selected a Revit element, but have not converted the element into Dynamo geometry. This is an important separation. If you were to select a large number of elements, you don't want to preview all of them in Dynamo because this would slow everything down. Dynamo is a tool to manage a Revit project without necessarily performing geometry operations, and we'll look at that in the next section of this chapter.
In this case, we're working with simple geometry, so we want to bring the geometry into the Dynamo preview. The "BldgMass" in the watch node above has a green number next to it. This represents the element's ID and tells us that we are dealing with a Revit element, not Dynamo geometry. The next step is to convert this Revit element into geometry in Dynamo.
Using the Element.Faces node, we get a list of surfaces representing each face of the Revit Mass. We can now see the geometry in the Dynamo viewport and start to reference the face for parametric operations.
Here's an alternative method. In this case, we're stepping away from selecting via the Revit Hierarchy ("All Elements of Category") and electing to explicitly select geometry in Revit.
Using the "Select Model Element" node, click the *"select" *(or "change") button. In the Revit viewport, select the desired element. In this case, we're selecting the building mass.
Rather than Element.Faces, we can select the full mass as one solid geometry using Element.Geometry. This selects all of the geometry contained within that mass.
Using Geometry.Explode, we can get the list of surfaces again. These two nodes work the same as Element.Faces but offer alternative options for delving into the geometry of a Revit element.
Using some basic list operations, we can query a face of interest.

First, output the selected elements from earlier to Element.Faces node.
Next, use the List.Count node reveals that we're working with 23 surfaces in the mass.
Referencing this number, we change the Maximum value of an *integer slider *to "22".
Using List.GetItemAtIndex, we input the lists and the *integer slider *for the index. Sliding through with the selected, we stop when we get to index 9 and have isolated the main facade hosts the trusses.
The previous step was a little cumbersome. We can do this much faster with the "Select Face" node. This allows us to isolate a face that is not an element itself in the Revit project. The same interaction applies as "Select Model Element", except we select the surface rather than the full element.
Suppose we want to isolate the main facade walls of the building. We can use the "Select Faces" node to do this. Click the "Select" button and then select the four main facades in Revit.
After selecting the four walls, make sure you click the "Finish" button in Revit.
The faces are now imported into Dynamo as surfaces.
Now, let's take a look at the beams over the atrium.
Use the "Select Model Element" node, select one of the beams.
Plug the beam element into the Element.Geometry node and we now have the beam in the Dynamo viewport.
We can zoom in on the geometry with a Watch3D node (if you don't see the beam in Watch 3D, right click and hit "zoom to fit").
A question that may come up often in Revit/Dynamo workflows: how do I select one element and get all similar elements? Since the selected Revit element contains all of its hierarchical information, we can query its family type and select all elements of that type.
Plug the beam element into a Element.ElementType node.
The Watch node reveals that the output is now a family symbol rather than a Revit element.
Element.ElementType is a simple query, so we can do this in the code block just as easily with
x.ElementType;
and get the same results.
To select the remaining beams, we use the "All Elements of Family Type" node.
The watch node shows that we've selected five Revit elements.
We can convert all of these five elements to Dynamo geometry too.
What if we had 500 beams? Converting all of these elements into Dynamo geometry would be really slow. If Dynamo is taking a long time to calculate nodes, you may want to use the "freeze" node functionality in order to pause the execution of Revit operations while you develop your graph. For more information on freezing nodes, check out the "Freezing" section in the solids chapter.
In any case, if we were to import 500 beams, do we need all of the surfaces to perform the intended parametric operation? Or can we extract basic information from the beams and perform generative tasks with fundamental geometry? This is a question that we'll keep in mind as we walk through this chapter. For example, let's take a look at the truss system next.
Using the same graph of nodes, select the truss element rather than the beam element. Before doing this, delete the Element.Geometry from the previous step.
Next we are ready to extract some basic information from trusses family type.
In the Watch node, we can see that we have a list of adaptive components selected from Revit. We want to extract the basic information, so we're start with the adaptive points.
Plug the "All Elements of Family Type" node into the "AdaptiveComponent.Location" node. This gives us a list of lists, each with three points which represent the adaptive point locations.
Connecting a "Polygon.ByPoints" node returns a polycurve. We can see this in the Dynamo viewport. By this method, we've visualized the geometry of one element and abstracted the geometry of the remaining array of elements (which could be larger in number than this example).
Tip: if you click on the green number of a Revit element in Dynamo, the Revit viewport will zoom to that element.
Roads, railways, land, utilities, surveying, GIS...
Civil infrastructure is all of these things, and more! This section contains several practical and relevant example graphs to help propel you towards Dynamo mastery and unlock the full potential of Dynamo for Civil 3D. Each graph comes complete with detailed descriptions of the logic that went into creating it so that you can not only use it, but understand it.
One of Dynamo's many great use cases is dynamically placing discrete objects along a Corridor model. It is often the case that objects need to be placed at locations that are independent of the inserted Assemblies along the Corridor, which is a very tedious task to accomplish manually. And when the horizontal or vertical geometry of the Corridor changes, then a significant amount of re-work is introduced.
Reading data from an external file (Excel in this case)
Organizing data in Dictionaries
Using Coordinate Systems to control position/scale/rotation
Placing Block References
Visualizing geometry in Dynamo
This graph will run on Civil 3D 2020 and above.
Start by downloading the sample files below and then opening the DWG file and Dynamo graph.
It is best if the Excel file is saved in the same directory as the Dynamo graph.
Here's an overview of the logic in this graph.
Read the Excel file and import the data into Dynamo
Get Feature Lines from the specified Corridor Baseline
Generate Coordinate Systems along the Corridor Feature Line at the desired stations
Use the Coordinate Systems to place Block References in Model Space
Let's go!
In this example graph, we're going to use an Excel file to store the data that Dynamo will use to place the light pole Block References. The table looks like this.
Using Dynamo to read data from an external file (such as an Excel file) is a great strategy, especially when the data needs to be shared with other team members.
The Excel data is imported into Dynamo like this.
Now that we have the data, we need to split it up by column (Corridor, Baseline, PointCode, etc.) so that it can be used in the rest of the graph. A common way to do this is to use the List.GetItemAtIndex node and specify the index number of each column that we want. For example, the Corridor column is at index 0, the Baseline column is at index 1, etc.
Seems fine, right? But there's a potential issue with this approach. What if the order of the columns in the Excel file changes in the future? Or a new column is added between two columns? Then the graph will not function properly and require an update. We can future-proof the graph by putting the data into a Dictionary, with the Excel column headers as the keys and the rest of the data as the values.
This makes the graph more resilient because it allows for flexibility in changing the order of the columns in Excel. As long as the column headers stay the same, then the data can simply be retrieved from the Dictionary using its key (i.e., the column header), which is what we do next.
Now that we have the Excel data imported and ready to go, let's start using it to get some information from Civil 3D about the Corridor models.
Select the Corridor model by its name.
Get a specific Baseline within the Corridor.
Get a Feature Line within the Baseline by its point code.
What we're going to do now is generate Coordinate Systems along the Corridor Feature Lines at the station values that we specified in the Excel file. These Coordinate Systems will be used to define the position, rotation, and scale of the light pole Block References.
Note the use of a Code Block here to rotate the Coordinate Systems depending on which side of the baseline they are on. This could be achieved using a sequence of several nodes, but this is a good example of a situation where it's easier to just write it out.
We're getting close! We have all the information we need to be able to actually place the Block References. The first thing to do is get the Block definitions that we want using the BlockName column in the Excel file.
From here, the last step is to create the Block References.
When you run the graph, you should see new Block References show up in Model Space along the Corridor. And here's the cool part - if the graph's execution mode is set to Automatic and you edit the Excel file, the Block References update automatically!
Here's an example of running the graph using Dynamo Player.
It can be helpful to visualize the Corridor geometry in Dynamo to provide context. This particular model has the Corridor solids already extracted in Model Space, so let's bring those into Dynamo.
But there's something else we need to consider. Solids are a relatively "heavy" geometry type, which means that this operation will slow down the graph. It would be nice if there was a simple way to choose if we wanted to view the solids or not. The obvious answer is to just unplug the Corridor.GetSolids node, but that will produce warnings for all of the downstream nodes, which is a little messy. This is a situation where the ScopeIf node really shines.
Notice that the Object.Geometry node has a gray bar at the bottom. This means that the node preview is turned off (accessible by right-clicking on the node), which allows the GeometryColor.ByGeometryColor to avoid "fighting" with other geometry for display priority in the background preview.
The ScopeIf node basically allows you to selectively run an entire branch of nodes. If the test input in false, then every node connected to the ScopeIf node will not run.
Here's the result in the Dynamo background preview.
Here are some ideas for how you could expand the capabilities of this graph.
Add a rotation column to the Excel file and use it to drive the rotation of the coordinate systems.
Add horizontal or vertical offsets to the Excel file so that the light poles could deviate from the Corridor Feature Line if needed.
Instead of using an Excel file with station values, generate the station values directly in Dynamo using a start station and typical spacing.
Now that you know a little more about the big picture, let's jump right in and build your first Dynamo graph in Civil 3D!
This is a simple example that is meant to demonstrate basic Dynamo functionality. It is recommended to work through the steps in a new, empty Civil 3D document.
The first thing to do is open up an empty document in Civil 3D. Once you're there, navigate to the Manage tab in the Civil 3D ribbon and look for the Visual Programming panel.
Click on the Dynamo button, which will launch Dynamo in a separate window.
What's the difference between Dynamo and Dynamo Player?
Dynamo is what you use to build and run graphs. Dynamo Player is an easy way to run graphs without having to open them in Dynamo.
Once Dynamo is open, you'll see the start screen. Click on New to open up a blank workspace.
What about the samples?
You should now be looking at an empty workspace. Let's see Dynamo in action! Here's our goal:
Pretty simple, right? But before we start, we need to cover a few fundamentals.
The core building blocks of a Dynamo graph are called nodes. A node is like a little machine - you put data into it, it does some work on that data, and it outputs a result. Dynamo for Civil 3D has a library of nodes that you can connect together with wires to form a graph that does bigger and better things than any one node can do by itself.
Wait, what if I've never used Dynamo before?
Some of this might be pretty new for you, and that's OK! These sections will help.
OK, let's build our graph. Here's a list of all the nodes that we'll need.
You can find these nodes by typing their names into the search bar in the library, or by right-clicking anywhere in the canvas and searching there.
How do I know which nodes to use and where to find them?
Here's what your final graph should look like.
Let's summarize what we've done here:
We chose which Document to work in. In this case (and many cases), we want to work in the active Document in Civil 3D.
We defined the destination Block where our Text object should be created (Model Space in this case).
We used a String node to specify which layer the Text should be placed on.
We created a point using the Point.ByCoordinates node to define the position where the Text should be placed.
We defined the X and Y coordinates of the Text insertion point using two Number Slider nodes.
We used another String node to define the contents of the Text object.
And finally, we created the Text object.
Let's see the results of our shiny new graph!
Back in Civil 3D, make sure that the Model tab is selected. You should see the new Text object that Dynamo created.
If you can't see the Text, you may need to run the ZOOM -> EXTENTS command to zoom to the right spot.
Cool! Now let's make some updates to the Text.
Back in your Dynamo graph, go ahead and change a few of the input values, such as the text string, insertion point coordinates, etc. You should see the Text automatically update in Civil 3D. Also notice that if you unplug one of the input ports, the Text is removed. If you plug everything back in, the Text is created again.
Why doesn't Dynamo insert a new Text object every time the graph is run?
This example just scratches the surface of what you can do with Dynamo for Civil 3D. Keep reading to learn more!
When adding Pipes and Structures to a Pipe Network, Civil 3D uses a template to automatically assign names. This is usually sufficient during initial placement, but inevitably the names will have to change in the future as the design evolves. In addition, there are many different naming patterns that might be required, for example naming Structures sequentially within a pipe run starting from the furthest downstream Structure, or following a naming pattern that aligns with a local agency's data schema. This example will demonstrate how Dynamo can be used to define any type of naming strategy apply it consistently.
Working with Bounding Boxes
Filtering data using the List.FilterByBoolMask node
Sorting data using the List.SortByKey node
Generating and modifying text strings
This graph will run on Civil 3D 2020 and above.
Start by downloading the sample files below and then opening the DWG file and Dynamo graph.
Here's an overview of the logic in this graph.
Select the Structures by layer
Get the Structure locations
Filter the Structures by offset, then sort them by station
Generate the new names
Rename the Structures
Let's go!
The first thing we need to do is select all of the Structures that we plan to work with. We'll do this by simply selecting all of the objects on a certain layer, which means that we can select Structures from different Pipe Networks (assuming they share the same layer).
This node ensures that we don't accidentally retrieve any undesirable object types that might share the same layer as the Structures.
Now that we have the Structures, we need to figure out their position in space so that we can sort them according to their location. To do this, we'll take advantage of the Bounding Box of each object. The Bounding Box of an object is the minimum-sized box that fully contains the geometric extents of the object. By computing the center of the Bounding Box, we get a pretty good approximation of the Structure's insertion point.
We'll use these points to get the station and offset of the Structures relative to a selected Alignment.
Here's where things start to get a little tricky. At this stage, we have a big list of all the Structures on the layer that we specified, and we chose an Alignment that we wanted to sort them along. The trouble is that there may be Structures in the list that we don't want to rename. For example, they may not be part of the particular run that we are interested in.
The selected Alignment
The Structures that we want to rename
The Structures that should be ignored
So, we need to filter the list of Structures so that we don't consider those that are greater than a certain offset from the Alignment. This is best accomplished using the List.FilterByBoolMask node. After filtering the list of Structures, we use the List.SortByKey node to sort them by their station values.
Check to see if the Structure's offset is less than the threshold value
Replace any null values with false
Filter the list of Structures and stations
Sort the Structures by the stations
The last bit of work that we need to do is create the new names for the Structures. The format that we'll use is <alignment name>-STRC-<number>
. There's a few extra nodes here to pad the numbers with extra zeros if desired (e.g., "01" instead of "1").
And, last but not least, we rename the Structures.
Here's an example of running the graph using Dynamo Player.
It can be helpful to take advantage of Dynamo's 3D background preview to visualize the graph's intermediate outputs instead of just the final result. One easy thing we can do is show the Bounding Boxes for the Structures. In addition, this particular dataset has a Corridor in the document, so we can bring the Corridor Feature Line geometry into Dynamo to provide some context for where the Structures are located in space. If the graph is used on a dataset that doesn't have any Corridors, then these nodes will simply do nothing.
Now we can better understand how the process of filtering the Structures by offset works.
Here are some ideas for how you could expand the capabilities of this graph.
Rename the structures based on their closest Alignment rather than selecting a specific Alignment.
Rename the Pipes in addition to the Structures.
Set the layers of the Structures based on their run.
We mentioned earlier that nodes are the core building blocks of a Dynamo graph, and they are organized into logical groups in the library. In Dynamo for Civil 3D, there are two categories (or shelves) in the library that contain dedicated nodes for working with AutoCAD and Civil 3D objects, such as Alignments, Profiles, Corridors, Block References, etc. The rest of the library contains nodes that are more generic in nature and are consistent between all "flavors" of Dynamo (for example, Dynamo for Revit, Dynamo Sandbox, etc.).
Specific nodes for working with AutoCAD and Civil 3D objects
General-purpose nodes
Nodes from third-party packages that you can install separately
By using the nodes found under the AutoCAD and Civil 3D shelves, your Dynamo graph will only work in Dynamo for Civil 3D. If a Dynamo for Civil 3D graph is opened elsewhere (in Dynamo for Revit, for example), these nodes will be flagged with a warning and will not run.
Why are there two separate shelves for AutoCAD and Civil 3D?
This organization distinguishes the nodes for native AutoCAD objects (Lines, Polylines, Block References, etc.) from the nodes for Civil 3D objects (Alignments, Corridors, Surfaces, etc.). And from a technical standpoint, AutoCAD and Civil 3D are two separate things - AutoCAD is the base application, and Civil 3D is built on top of it.
In order to work with the AutoCAD and Civil 3D nodes, it's important to have a solid understanding of the object hierarchy within each shelf. Remember the taxonomy from Biology? Kingdom, Phylum, Class, Order, Family, Genus, Species? AutoCAD and Civil 3D objects are categorized in a similar manner. Let's work through some examples to explain.
Let's use an Alignment as an example.
Say your goal is to change the name of the Alignment. From here, the next node you would add is a CivilObject.SetName node.
At first, this may not seem very intuitive. What is a CivilObject, and why does the library not have an Alignment.SetName node? The answer is related to reusability and simplicity. If you think about it, the process of changing the name of a Civil 3D object is the same whether the object is an Alignment, Corridor, Profile, or something else. So instead of having repetitive nodes that essentially all do the same thing (e.g., Alignment.SetName, Corridor.SetName, Profile.SetName, etc.), it would make sense to wrap up that functionality into a single node. That's exactly what CivilObject.SetName does!
Another way to think about this is in terms of relationships. An Alignment and a Corridor are both types of Civil Objects, just like an apple and a pear are both types of fruit. Civil Object nodes apply to any type of Civil Object, just like you might want to use a single peeler for peeling both an apple and a pear. Your kitchen would get pretty messy if you had a separate peeler for every type of fruit! In that sense, the Dynamo node library is just like your kitchen.
Now, let's take this one step further. Say you want to change the layer of the Alignment. The node you would use is the Object.SetLayer node.
Why isn't there a node called CivilObject.SetLayer? The same principles of reusability and simplicity that we discussed earlier apply here. The layer property is something that is common to any object in AutoCAD that can be drawn or inserted, such a Line, Polyline, Text, Block Reference, etc. Civil 3D objects like Alignments and Corridors fall under the same category, and so any node that applies to an Object can also be used with any Civil Object.
The engineering design of a typical housing development involves working with several underground utilities, such as sanitary sewer, storm drainage, potable water, or others. This example will demonstrate how Dynamo can be used to draw the service connections from a distribution main to a given lot (i.e., parcel). It is common for every lot to require a service connection, which introduces significant tedious work to place all of the services. Dynamo can speed up the process by automatically drawing the necessary geometry with precision, as well as providing flexible inputs that can be adjusted to suit local agency standards.
Using the Select Object node for user input
Working with Coordinate Systems
Using geometric operations like Geometry.DistanceTo and Geometry.ClosestPointTo
Creating Block References
Controlling object binding settings
This graph will run on Civil 3D 2020 and above.
Start by downloading the sample files below and then opening the DWG file and Dynamo graph.
Here's an overview of the logic in this graph.
Get the curve geometry for the distribution main
Get the curve geometry for a user-selected lot line, reversing if necessary
Generate the insertion points for the service meters
Get the points on the distribution main that are closest to the service meter locations
Create Block References and Lines in Model Space
Let's go!
Our first step is to get the geometry for the distribution main into Dynamo. Instead of selecting individual Lines or Polylines, we'll instead get all of the objects on a certain layer and join them together as a Dynamo PolyCurve.
Next, we need to get the geometry for a selected lot line into Dynamo so we can work with it. The right tool for the job is the Select Object node, which allows the user of the graph to pick a specific object in Civil 3D.
We also need to handle a potential issue that may arise. The lot line has a start point and an end point, which means that it has a direction. In order for the graph to produce consistent results, we need all of the lot lines to have a consistent direction. We can account for this condition directly in the graph logic, which makes the graph more resilient.
Get the start and end points of the lot line.
Measure the distance from each point to the distribution main, then figure out which distance is greater.
The desired result is that the start point of the line is closest to the distribution main. If that isn't then case, then we reverse the direction of the lot line. Otherwise we simply return the original lot line.
It's time to figure out where the service meters are going to be placed. Typically the placement is determined by local agency requirements, so we'll just provide input values that can be changed to suit various conditions. We're going to use a Coordinate System along the lot line as the reference for creating the points. This makes it really easy to define offsets relative to the lot line, not matter its orientation.
Now we need to get points on the distribution main that are closest to the service meter locations. This will allow us to draw the service connections in Model Space so that they are always perpendicular to the distribution main. The Geometry.ClosestPointTo node is the perfect solution.
This is the distribution main PolyCurve
These are the service meter insertion points
The last step is to actually create objects in Model Space. We'll use the insertion points that we generated previously to create Block References, and then we'll use the points on the distribution main to draw Lines to the service connections.
When you run the graph you should see new Block References and service connection lines in Model Space. Try changing some of the inputs and watching everything update automatically!
You may notice that after placing the objects for one lot line, selecting a different lot line results in the objects being "moved."
This is Dynamo's default behavior, and it is very useful in many cases. However, you may find want to place several service connections sequentially and have Dynamo create new objects with each run instead of modifying the original ones. You can control this behavior by changing the object binding settings.
Changing this setting will force Dynamo to "forget" the objects that it creates with each run. Here's an example of running the graph with object binding turned off using Dynamo Player.
Here are some ideas for how you could expand the capabilities of this graph.
Place multiple service connections simultaneously instead of selecting each lot line.
Adjust the inputs to instead place sewer cleanouts instead of water service meters.
Add a toggle to allow for placing a single service connection on a particular side of the lot line instead of both sides.
Working with COGO Points and Point Groups in Civil 3D is a core element of many field-to-finish processes. Dynamo really shines when it comes to data management, and we'll demonstrate one potential use case in this example.
Working with Lists
Grouping similar objects with the List.GroupByKey node
Showing custom output in Dynamo Player
This graph will run on Civil 3D 2020 and above.
Start by downloading the sample files below and then opening the DWG file and Dynamo graph.
Here's an overview of the logic in this graph.
Get all of the COGO Points in the Document
Group the COGO Points by description
Create Point Groups
Output a summary to Dynamo Player
Let's go!
Our first step to get all of the Point Groups in the Document, then get all of the COGO Points within each group. This will give us a nested list or "list of lists," which will be easier to work with later if we flatten everything down to a single list with the List.Flatten node.
Now that we have all the COGO Points, we need to separate them into groups based on their descriptions. This is exactly what the List.GroupByKey node does. It essentially groups together any items that share the same key.
The hard work is done! The final step is to create new Civil 3D Point Groups from the grouped COGO Points.
When you run the graph, there's nothing to see in the Dynamo background preview because we aren't working with any geometry. So the only way to see if the graph executed properly is to check the Toolspace, or to look at the node output previews. However, if we run the graph using Dynamo Player, then we can provide more feedback about the graph's results by outputting a summary of the Point Groups that were created. All you have to do is right-click on a node and set it to Is Output. In this case, we use a renamed Watch node to view the results.
Here's an example of running the graph using Dynamo Player.
Here are some ideas for how you could expand the capabilities of this graph.
Modify the point grouping to be based on full description instead of raw description.
Group the points by some other pre-defined categories that you choose (e.g., "Ground shots," "Monuments," etc.)
Automatically create TIN Surfaces for points in certain groups.
Developing kinematic envelopes for clearance validation is an important part of rail design. Dynamo can be used to generate solids for the envelope instead of creating and managing complex Corridor subassemblies to do the job.
Working with Corridor Feature Lines
Transforming geometry between Coordinate Systems
Creating solids by lofting
Controlling node behavior with lacing settings
This graph will run on Civil 3D 2020 and above.
Start by downloading the sample files below and then opening the DWG file and Dynamo graph.
Here's an overview of the logic in this graph.
Get Feature Lines from the specified Corridor Baseline
Generate Coordinate Systems along the Corridor Feature Line at the desired spacing
Transform the profile Block geometry to the Coordinate Systems
Loft a solid between the profiles
Create the solids in Civil 3D
Let's go!
Our first step is to get Corridor data. We'll select the Corridor model by its name, get a specific Baseline within the Corridor, and then get a Feature Line within the Baseline by its point code.
What we're going to do now is generate Coordinate Systems along the Corridor Feature Lines between a given start and end station. These Coordinate Systems will be used to align the vehicle profile Block geometry to the Corridor.
Notice the little XXX in the bottom-right corner of the node. This means that the node's lacing settings are set to Cross Product, which is necessary to generate the Coordinate Systems at the same station values for both Feature Lines.
Now we need to somehow create an array of the vehicle profiles along the Feature Lines. What we're going to do is transform the geometry from the vehicle profile Block definition using the Geometry.Transform node. This is a tricky concept to visualize, so before we look at the nodes, here's a graphic that shows what is going to happen.
So essentially we're taking the Dynamo geometry from a single Block definition and moving/rotating it, all while creating an array along the Feature Line. Cool stuff! Here's what the node sequence looks like.
This gets the Block definition from the Document.
These nodes get the Dynamo geometry of the Objects within the Block.
These nodes essentially define the Coordinate System that we are transforming the geometry from.
And finally, this node does the actual work of transforming the geometry.
Note the Longest lacing on this node.
And here's what we get in Dynamo.
Good news! The hard work is done. All we need to do now is generate solids between the profiles. This is easily accomplished with the Solid.ByLoft node.
And here's the result. Remember that these are Dynamo solids - we still need to create them in Civil 3D.
Our final step is to output the generated solids into Model Space. We'll also give them a color to make them very easy to see.
Here's an example of running the graph using Dynamo Player.
Here are some ideas for how you could expand the capabilities of this graph.
Add the ability to use different station ranges separately for each track.
Split the solids into smaller segments that could be analyzed individually for clashes.
Check to see if the envelope solids intersect with features and color those that clash.
Dynamo offers a vast number of features out of the box and also maintains an extensive package library that can significantly extend Dynamo’s capability. A package is a collection of custom nodes or additional functionality. The Dynamo Package Manager is a portal for the community to download any package that has been published online. These toolsets are developed by third parties to extend Dynamo's core functionality, accessible to all, and ready to download at the click of the button.
An open-source project such as Dynamo thrives on this type of community involvement. With dedicated third-party developers, Dynamo is able to extend its reach to workflows across a range of industries. For this reason, the Dynamo team has made concerted efforts to streamline package development and publishing (which will be discussed in more detail in the following sections).
The easiest way to install a package is by using the Packages menu option in your Dynamo interface. Let's jump right into it and install a package now. In this quick example, we'll install a popular package for creating quad panels on a grid.
In Dynamo, go to Packages > Package Manager...
In the search bar, let's search for "quads from rectangular grid". After a few moments, you should see all of the packages which match this search query. We want to select the first package with the matching name.
Click Install to add this package to your library, then accept the confirmation. Done!
Notice that we now have another group in our Dynamo library called "buildz". This name refers to the developer of the package, and the custom node is placed in this group. We can begin to use this right away.
Use Code Block to quickly define a rectangular grid, output the result to a Polygon.ByPoints Node, subsequently a Surface.ByPatch Node to view the list of rectangular panels you have just created.
The example above focuses on a package with one custom node, but you use the same process for downloading packages with several custom nodes and supporting data files. Let's demonstrate that now with a more comprehensive package: Dynamo Unfold.
As in the example above, begin by selecting Packages > Package Manager...
This time, we'll search for "DynamoUnfold", one word. When we see the packages, download by clicking on Install to add Dynamo Unfold to your Dynamo Library.
In the Dynamo Library, we have a DynamoUnfold Group with multiple categories and custom nodes.
Now, let's take a look at the package's file structure.
First, go to Packages > Package Manager > Installed Packages.
Then, click Show Root Directory to open the root folder for this package.
This will take us to the package's root directory. Notice that we have 3 folders and a file.
The bin folder houses .dll files. This Dynamo package was developed using Zero-Touch, so the custom nodes are held in this folder.
The dyf folder houses the custom nodes. This package was not developed using Dynamo custom nodes, so this folder is empty for this package.
The extra folder houses all additional files, including our example files.
The pkg file is a basic text file defining the package settings. We can ignore this for now.
Opening the "extra" folder, we see a bunch of example files that were downloaded with the install. Not all packages have example files, but this is where you can find them if they are part of a package.
Let's open up "SphereUnfold".
After opening the file and hitting "Run" on the solver, we have an unfolded sphere! Example files like these are helpful for learning how to work with a new Dynamo package.
In the Package Manager, you can browse for packages by using the sorting and filtering options in the Search for Packages tab. There are several filters available for host program, status (new, deprecated, or undeprecated), and whether or not the package has dependencies.
By sorting the packages, you can identify highly rated or most downloaded packages, or find packages with recent updates.
You can also access more detail on each package by clicking View Details. This opens a side panel in the Package Manager, where you can find information such as versioning and dependencies, website or repository URL, license information, etc.
If you would like to see where your package files are kept, in the top navigation click on Dynamo > Preferences > Package Settings > Node and Package File Locations, you can find your current root folder directory from here.
By default, packages are installed in a location similar to this folder path: C:/Users/[username]/AppData/Roaming/Dynamo/[Dynamo Version].
For users who are asking if it is possible to deploy Dynamo (in any form) with pre-attached packages: The approach that will solve this issue and allow for control at a central location for all users with Dynamo installs is to add a custom package path to each installation.
Adding a network folder where the BIM manager or others could supervise the stocking of the folder with office approved packages
In the UI of an individual application, go to Dynamo -> Preferences -> Package Settings -> Node and Package file locations. In the dialog, press the "Add Path" button and browse to the network location for the shared package resource.
As an automated process, it would involve adding information to the configuration file that is installed with Dynamo:
C:\Users\[Username]\AppData\Roaming\Dynamo\Dynamo Revit\[Dynamo Version]\DynamoSettings.xml
By default, the configuration for Dynamo for Revit is:
<CustomPackageFolders>
<string>C:\Users\[Username]\AppData\Roaming\Dynamo\Dynamo Revit\[Dynamo Version]</string>
</CustomPackageFolders>
Adding a custom location would look like:
<CustomPackageFolders>
<string>C:\Users\[Username]\AppData\Roaming\Dynamo\Dynamo Revit\[Dynamo Version]</string>
<string>N:\OfficeFiles\Dynamo\Packages_Limited</string>
</CustomPackageFolders>
The central management for this folder can also be controlled by simply making the folder read only.
An organization might want to standardize the packages installed by different workstations and users. A way to do this, could be to install these packages from Dynamo -> Preferences -> Package Settings -> Node and Package file locations, selecting a network folder as the install location, and get workstations to add that path to Manage Node and Package Paths
.
The Dynamo community is constantly growing and evolving. By exploring the Dynamo Package Manager from time to time, you'll find some exciting new developments. In the following sections, we'll take a more in-depth look at packages, from the end-user perspective to authorship of your own Dynamo Package.
Furthermore, these examples embody time-tested best practices for building strong graphs. As you work through the examples, we encourage you to also familiarize yourself with the section for more ideas on how to build powerful, flexible, and maintainable graphs.
Place light pole Block References along a Corridor at station values specified in an Excel file.
If Dictionaries are new to you, take a look at the section.
If Coordinate Systems are new to you, take a look at the section.
If Code Blocks are new to you, take a look at the section.
You can read more about graph execution modes in the section.
If Dynamo Player is new to you, take a look at the section.
Mission accomplished!
Head to the section when you're ready to try it out.
Dynamo for Civil 3D comes with a few pre-built graphs that can help spark some more ideas for how you can use Dynamo. We recommend taking a look at those at some point, as well as the here in the Primer.
Build a Dynamo graph that will insert Text into Model Space.
Nodes in the library are grouped into logical categories based on what they do. Take a look at the section for a more in-depth tour.
By default, Dynamo will "remember" the objects that it creates. If you change the node input values, the objects in Civil 3D are updated instead of creating brand new objects. You can read more about this behavior in the section.
Mission accomplished!
Rename Pipe Network Structures in order based on the stationing of an Alignment.
If you're new to working with Lists, take a look at the section.
If Dynamo Player is new to you, take a look at the section.
Mission accomplished!
Check out the section for more information about how the nodes in the core Dynamo library are organized.
Place water service meter Block References at specified offsets from a lot line, and draw a Line for each service connection perpendicular to the distribution main.
If Dynamo curve geometry is new to you, take a look at the section.
If Coordinate Systems are new to you, take a look at the section.
Take a look at the section for more information.
If Dynamo Player is new to you, take a look at the section.
Mission accomplished!
Create a Point Group for each unique COGO Point description.
If you're new to working with Lists, take a look at the section.
If Dynamo Player is new to you, take a look at the section.
Mission accomplished!
Use a vehicle profile Block to generate clearance envelope 3D solids along a Corridor.
If Coordinate Systems are new to you, take a look at the section.
If node lacing is new to you, take a look at the section.
If Dynamo Player is new to you, take a look at the section.
Mission accomplished!
Next to DynamoUnfold, select the options menu .
Another way to discover Dynamo packages is to explore the website. Here, you can find package dependencies and host/version compatibility information provided by Package Authors. You can also download the package files from the Dynamo Package Manager, but doing so directly from Dynamo is a more seamless process.
While the scenario works properly for packages that contain only custom nodes, it might not work for packages containing binaries, like zero-touch nodes. This issue is caused by the .NET framework places over loading assemblies when they come from a network location. Unfortunately, using the loadFromRemoteSources
configuration element, as suggested in the linked thread, is not a possible solution for Dynamo, because it is distributed as a component rather than an application.
One possible workaround is to use a mapped network drive pointing to the network location, and have workstations reference that path instead. The steps to create a mapped network drive are described .
Over time, you may find yourself needing to go beyond the basics and dive into Dynamo's inner workings. The pages in this section will give you a blueprint for unlocking advanced functionality within Dynamo for Civil 3D so that you can take your graphs to the next level.
Dynamo is available as an extension in Forma. At the moment, it comes in two flavors: a desktop-connected version, and a cloud-based integration as a closed beta. Read more about the desktop version in this blog post, and about the closed beta cloud integration in this blog post.
Autodesk Forma is a cloud software that offers powerful, yet easy-to-use AI-powered tools for pre-design and schematic design. Architects and designers use Forma to model complex 3D designs in minutes, optimize living quality and sustainability through real-time environmental analysis, and continue the design process by fluidly connecting with Revit, Rhino, and Dynamo. You can learn more and start your free trial or purchase here: Autodesk Forma. Forma is included as part of the AEC Collection.
Dynamo Player is an extension that brings a variety of design automation capabilities to Forma users. Some key advantages of using Dynamo Player with Forma:
Streamlined Automation: Dynamo Player simplifies the process of automating repetitive tasks and workflows in Forma. Custom graphs created with Dynamo’s visual programming environment can be executed directly within Forma with just a few clicks. The graphs are displayed as a list within Dynamo Player, making it easy to select and run them whenever needed. This streamlined process allows you to quickly execute repetitive tasks, reducing errors and boosting efficiency.
Easy-to-Use Interface: Dynamo Player provides a user-friendly interface that makes it accessible to Forma users, with no Dynamo expertise required. In the future, we plan on releasing more sample graphs that give you access to Dynamo-powered automations without having to rely on developers or advanced coding skills.
Customizable Parameters: Dynamo Player allows you to define input parameters for your automation graphs, enabling you to customize graph behavior based on your specific project requirements. You can set up options and values that can be easily modified each time the graph is executed, providing flexibility and adaptability to different scenarios.
Reusability and Sharing: Dynamo Player promotes collaboration and knowledge sharing among Forma users. Automation graphs can be saved and reused across multiple sites, ensuring consistency and efficiency. Additionally, you can share your scripts with others in the Forma community, enabling them to benefit from your automation solutions and vice versa.
While Dynamo is extremely powerful as a visual programming tool, it is also possible to go beyond nodes and wires and write code in textual form. There are two ways that you can do this:
Write DesignScript using a Code Block
Write Python using a Python node
This section will focus on how to leverage Python in the Civil 3D environment to take advantage of the AutoCAD and Civil 3D .NET APIs.
Take a look at the Python section for more general information about using Python in Dynamo.
Both AutoCAD and Civil 3D have several APIs available that enable developers like you to extend the core product with custom functionality. In the context of Dynamo, it is the Managed .NET APIs that are relevant. The following links are essential to understanding the structure of the APIs and how they work.
AutoCAD .NET API Developer's Guide
AutoCAD .NET API Reference Guide
Civil 3D .NET API Developer's Guide
Civil 3D .NET API Reference Guide
As you move through this section, there may be some concepts that you are unfamiliar with, such as databases, transactions, methods, properties, etc. Many of these concepts are core to working with the .NET APIs and are not specific to Dynamo or Python. It is beyond the scope of this section of the Primer to discuss these items in detail, so we recommend frequently referring to the above links for more information.
When you first edit a new Python node, it will be pre-populated with template code to get you started. Here's a breakdown of the template with explanations about each block.
Imports the
sys
andclr
modules, both of which are necessary for the Python interpreter to function properly. In particular, theclr
module enables .NET namespaces to be treated essentially as Python packages.Loads the standard assemblies (i.e., DLLs) for working with the managed .NET APIs for AutoCAD and Civil 3D.
Adds references to standard AutoCAD and Civil 3D namespaces. These are equivalent to the
using
orImports
directives in C# or VB.NET (respectively).The node's input ports are accessible using a pre-defined list called
IN
. You can access the data in a specific port using its index number, for exampledataInFirstPort = IN[0]
.Gets the active Document and Editor.
Locks the Document and initiates a Database transaction.
This where you should place the bulk of your script's logic.
Uncomment this line to commit the transaction after your main work is done.
If you want to output any data from the node, assign it to the
OUT
variable at the end of your script.
Want to customize?
You can modify the default Python template by editing the PythonTemplate.py
file located in C:\ProgramData\Autodesk\C3D <version>\Dynamo
.
Let's work through an example to demonstrate some of the essential concepts of writing Python scripts in Dynamo for Civil 3D.
Here are examples files that you can reference for this exercise.
Here's an overview of the logic in this graph.
Review the Civil 3D API documentation
Select all of the Catchments in the document by layer name
"Unwrap" the Dynamo Objects to access the internal Civil 3D API members
Create Dynamo points from AutoCAD points
Create PolyCurves from the points
Let's go!
Before we start building our graph and writing code, it's a good idea to take a look at the Civil 3D API documentation and get a sense of what the API makes available to us. In this case, there's a property in the Catchment class that will return the boundary points of the Catchment. Note that this property returns a Point3dCollection
object, which is not something that Dynamo will know what to do with. In other words, we won't be able to create a PolyCurve from a Point3dCollection
, so eventually we'll need to convert everything to Dynamo points. More on that later.
Now we can start building our graph logic. The first thing to do is get a list of all the Catchments in the Document. There are nodes available for this, so we don't need to include it in the Python script. Using nodes provides better visibility for someone else that might read the graph (versus burying lots of code in a Python script), and it also keeps the Python script focused on one thing: returning the boundary points of the Catchments.
Note here that the output from the All Objects on Layer node is a list of CivilObjects. This is because Dynamo for Civil 3D doesn't currently have any nodes for working with Catchments, which is the whole reason why we need to access the API through Python.
Before we go further, we need to briefly touch on an important concept. In the Node Library section, we discussed how Objects and CivilObjects are related. To add a little more detail to this, a Dynamo Object is a wrapper around an AutoCAD Entity. Similarly, a Dynamo CivilObject is a wrapper around a Civil 3D Entity. You can "unwrap" an Object by accessing its InternalDBObject
or InternalObjectId
properties.
Object Autodesk.AutoCAD.DynamoNodes.Object
Entity Autodesk.AutoCAD.DatabaseServices.Entity
CivilObject Autodesk.Civil.DynamoNodes.CivilObject
Entity Autodesk.Civil.DatabaseServices.Entity
As a rule of thumb, it is generally safer to get the Object ID using the InternalObjectId
property and then access the wrapped object in a transaction. This is because the InternalDBObject
property will return an AutoCAD DBObject that is not in a writable state.
Here's the complete Python script that does the work of accessing the internal Catchment objects are getting their boundary points. The highlighted lines represent those that are modified/added from the default template code.
Click on the underlined text in the script for an explanation of each line.
As a rule of thumb, it is a best practice to include the bulk of your script logic inside a transaction. This ensures safe access to the objects that your script is reading/writing. In many cases, omitting a transaction can cause a fatal error.
At this stage, the Python script should output a list of Dynamo points that you can see in the background preview. The last step is to simply create PolyCurves from the points. Note that this could also be accomplished directly in the Python script, but we've intentionally put it outside the script in a node so that it is more visible. Here's what the final graph looks like.
And here's the final Dynamo geometry.
Just a quick note here before we wrap up. Depending on which version of Civil 3D you are using, the Python node may be configured differently. In Civil 3D 2020 and 2021, Dynamo used a tool called IronPython to move data between .NET objects and Python scripts. In Civil 3D 2022, however, Dynamo transitioned to use the standard native Python interpreter (aka CPython) instead that uses Python 3. Benefits of this transition include access to popular modern libraries and new platform features, essential maintenance, and security patches.
You can read more about this transition and how to upgrade legacy scripts on the Dynamo Blog. If you want to keep using IronPython, then you will simply need to install the DynamoIronPython2.7 package using the Dynamo Package Manager.
Dynamo packages are toolsets developed by third parties in order to extend Dynamo's core functionality. They are accessible to all and ready to download at the click of the button.
Refer to the Packages section for more information about how to get started with packages.
Here's a list of some of the most popular packages that can take your Dynamo for Civil 3D graphs to the next level.
The Civil 3D Toolkit is a Dynamo for Civil 3D package that provides significant enhancements to Dynamo's capabilities through a large inventory of additional nodes.
Camber is an open source Dynamo for Civil 3D package that includes hundreds of nodes for working with Labels, Xrefs, Data Shortcuts, Styles, and more.
CivilConnection is a open source Dynamo for Revit package that enables the exchange of information between Civil 3D, Dynamo, and Revit.
Arkance Systems Nodes is a Dynamo for Civil 3D package with a wide variety of helpful nodes for working with Dimensions, Tables, Views, Drill Control, and more.
When you open a graph, nodes marked as Is Input show up in Player, and you can interact with them.
For some inputs, you will be taken to a selection mode where you can select elements in the scene. Click Confirm selection to accept the selection and go back to view the graph inputs.
Click Run in Player to run your graph. Once it's done, Watch and Watch 3D nodes marked as Is Output will be displayed as follows:
Graphs that generate geometry give you the option to preview and add the geometry to your site. Click the eye icon to turn preview on and off, then click Add to add the geometry to your site.
Once you have set up Dynamo Player, you can open files in Player. If you're using the desktop version, you'll also need to have Dynamo open.
If you are using Dynamo Desktop, Player will recognize a graph that's currently open in Dynamo and give you the option to open it in Player.
On both the Service and Desktop tabs, you'll find an area labeled Upload graph [ 1 ] where you can drag and drop .dyn files to add them to Player. Alternatively, click the area to open a dialog to browse and add files.
Graphs you upload appear under Uploaded graphs [ 2 ]. You can open the graph or click the options menu (three dots) to remove, download, or share the graph.
Graphs can be shared to a site or a hub [ 3 ]. A hub includes multiple sites. To share a graph, click Share graph and enter graph details. You can choose whether to share the graph to the site or hub. Once you click Share, the graph becomes available to other users with access to that site or hub.
Dynamo Player comes with several pre-built graphs in the Graphs provided by Autodesk section [ 4 ]. These are a great starting point for exploration and experimentation!
The code block is a unique feature in Dynamo that dynamically links a visual programming environment with a text-based one. The code-block has access to all of the Dynamo nodes and can define an entire graph in one node. Read this chapter closely, as the code block is a fundamental building block of Dynamo.
This page highlights the differences you should be aware of when writing Dynamo programs to execute in the Dynamo compute service cloud context.
DaaS, Dynamo as a Service, Dynamo compute service, etc. all refer to the same thing: Dynamo's core runtime executing in a cloud context. This means your graph is not executing on your machine. DaaS can currently be accessed only through the Dynamo Player extension for Forma, where users can upload and manage .dyn
files created in the desktop environment, run .dyn
files shared by their colleagues through the extension, or use pre-loaded .dyn
routines provided by Autodesk as samples.
Because your graphs run in this cloud context, and not on your machine, DaaS currently cannot directly use traditional Dynamo host contexts (Revit, Civil 3D, etc.). If you want to use types from those programs in your graph, you will need to serialize (save) them into the graph using the Data.Remember
node or other in-graph serialization techniques. These are similar to the workflows you need to use when writing graphs for Generative Design in Revit.
The version is based on 3.x and is updated frequently based on Dynamo's open-source master branch.
Most of the core nodes, see the next section for some specific limitations.
DynamoFormaBeta
package for interacting with the Forma API.
VASA
for voxelization / efficient analysis.
MeshToolKit
for mesh manipulation. Mesh toolkit is also available out-of-the-box starting in Dynamo 3.4.
RefineryToolkit
for useful algorithms that allow clash test, view distance, shortest path, isovist, etc.
Python nodes will not work. These will currently just fail to execute.
Custom packages cannot be used.
The UI/view layer of UI nodes will not execute. We don't anticipate this to be a problem with core functionality, but it's good to keep in mind if you see a bug related to a node with custom UI.
Windows-only functionality will not work. For example, if you try to use the Windows registry or WPF, this will fail.
View extensions will not be loaded.
Filesystem nodes are not going to be very useful. Any files you reference on your local machine will not exist when running in DaaS.
Excel/DSOffice interop nodes will not function. Open XML nodes should function.
Network requests in general will not function, though you can make calls to the Forma API.
In the future, we intend to provide tooling inside Dynamo for the desktop that will make it easier to ensure your graph runs the same in both contexts.
During this Beta, compute time is not currently charged.
Getting access to Autodesk Forma.
Installing the DynamoFormaBeta for Dynamo on Desktop and the Dynamo Extension in Forma.
Writing your first graph.
Be aware your shared graphs are stored in Forma.
Max graph execution time is currently less than 30 minutes. This value may change.
Execution requests are rate limited, so you may see errors if you make many compute requests in too short a period.
To use Dynamo with Forma, you have two options: cloud-based Dynamo as a Service (DaaS), or desktop Dynamo. Each has its benefits depending on what you want to do, so before you get started, consider which option best serves your needs. However, keep in mind that you can switch between these options at any time.
Dynamo as a Service and Dynamo Desktop comparison
On this page, we'll explain how to set up both options.
Dynamo in Forma Beta is currently available as an early access open beta, which means that features and the user interface may change frequently.
First, let's install Dynamo Player in Forma.
In your Forma site, go to Extensions in the left sidebar and click Add extension. This opens the Autodesk App Store.
Search for Dynamo, and add Dynamo Player Beta. Read the disclaimer and click Agree.
Now, Dynamo Player is available in your Extensions. Click it to open it.
You are now ready to use Dynamo Player!
To use Dynamo Desktop, you’ll need Dynamo, either as a stand-alone Sandbox or connected to Revit or Civil 3D. You'll also need the DynamoFormaBeta package.
Follow these directions to set up Dynamo in Revit and the DynamoFormaBeta package.
Make sure you have Revit 2024.1 or higher installed.
Open Dynamo from Revit by going to Manage > Dynamo.
In Dynamo, install the DynamoFormaBeta package. Go to Packages > Package Manager, then search for DynamoFormaBeta.
If you have Revit 2024, install the DynamoFormaBeta for 2.x package.
If you have Revit 2025, install the DynamoFormaBeta package.
Follow these directions to set up Dynamo in Civil 3D and the DynamoFormaBeta package.
Make sure you have Civil 3D 2024.1 or higher installed.
Open Dynamo from Civil 3D by going to Manage > Dynamo.
In Dynamo, install the DynamoFormaBeta package. Go to Packages > Package Manager, then search for DynamoFormaBeta.
If you have Civil 3D 2024, install the DynamoFormaBeta for 2.x package.
If you have Civil 3D 2025, install the DynamoFormaBeta package.
Follow these directions to install Dynamo Sandbox and the DynamoFormaBeta package.
The Daily versions are development versions and may include incomplete or in-progress features.
Run DynamoSandbox.exe from the Dynamo install folder.
In Dynamo, install the DynamoFormaBeta package. Go to Packages > Package Manager, then search for DynamoFormaBeta.
If you have Dynamo 2.x, install the DynamoFormaBeta for 2.x package.
If you have Dynamo 3.x, install the DynamoFormaBeta package.
Once Dynamo is installed, you are ready to use it with Forma. When running the desktop option of Dynamo in Forma, you’ll need to have Dynamo open to be able to use the Dynamo Player extension.
First, let's install Dynamo Player in Forma.
In your Forma site, go to Extensions in the left sidebar and click Add extension. This opens the Autodesk App Store.
Search for Dynamo, and add Dynamo Player Beta. Read the disclaimer and click Agree.
Now, Dynamo Player is available in your Extensions. Click it to open it.
Near the top, click Desktop to access Dynamo Desktop.
You are now ready to use Dynamo Player! If you already have a graph open in Dynamo, just click Open under Connected graph to view it in Player.
Functions can be created in a code block and recalled elsewhere in a Dynamo definition. This creates another layer of control in a parametric file, and can be viewed as a text-based version of a custom node. In this case, the "parent" code block is readily accessible and can be located anywhere on the graph. No wires needed!
The first line has the key word “def”, then the function name, then the names of inputs in parentheses. Braces define the body of the function. Return a value with “return =”. Code Blocks that define a function do not have input or output ports because they are called from other Code Blocks.
Call the function with another Code Block in the same file by giving the name and the same number of arguments. It works just like the out-of-the-box nodes in your library.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this exercise, we will make a generic definition that will create spheres from an input list of points. The radius of these spheres are driven by the Z property of each point.
Let's begin with a number range of ten values spanning from 0 to 100. Plug these into a Point.ByCoordinates nodes to create a diagonal line.
Create a Code Block and introduce our definition.
Use these lines of code:
The inputPt is the name we've given to represent the points that will drive the function. As of now, the function isn't doing anything, but we'll build up this function in the steps to come.
Adding to the Code Block function, we place a comment and a sphereRadius variable which queries the Z position of each point. Remember, inputPt.Z does not need parentheses as a method. This is a query of an existing element's properties, so no inputs are necessary:
Now, let's recall the function we've created in another Code Block. If we double-click on the canvas to create a new code block, and type in sphereB, we notice that Dynamo suggest the sphereByZ function that we've defined. Your function has been added to the intellisense library! Pretty cool.
Now we call the function and create a variable called Pt to plug in the points created in the earlier steps:
We notice from the output that we have all null values. Why is this? When we defined the function, we are calculating the sphereRadius variable, but we did not define what the function should return as an output. We can fix this in the next step.
An important step, we need to define the output of the function by adding the line
return = sphereRadius;
to the sphereByZ function.Now we see that the output of the Code Block gives us the Z coordinates of each point.
Let's create actual spheres now by editing the Parent function.
We first define a sphere with the line of code:
sphere=Sphere.ByCenterPointRadius(inputPt,sphereRadius);
Next, we change the return value to be the sphere instead of the sphereRadius:
return = sphere;
This gives us some giant spheres in our Dynamo preview!
1. To temper the size of these spheres, let's update the sphereRadius value by adding a divider:
sphereRadius = inputPt.Z/20;
Now we can see the separate spheres and start to make sense of the relationship between radius and Z value.
On the Point.ByCoordinates node, by changing the lacing from Shortest List to Cross Product, we create a grid of points. The sphereByZ function is still in full effect, so the points all create spheres with radii based on Z values.
And just to test the waters, we plug the original list of numbers into the X input for Point.ByCoordinates. We now have a cube of spheres.
Note: if this takes a long time to calculate on your computer, try to change #10 to something like #5.
Remember, the sphereByZ function we've created is a generic function, so we can recall the helix from an earlier lesson and apply the function to it.
One final step: let's drive the radius ratio with a user defined parameter. To do this, we need to create a new input for the function and also replace the 20 divider with a parameter.
Update the sphereByZ definition to:
Update the children Code Block by adding a ratio variable to the input:
sphereByZ(Pt,ratio);
Plug a slider into the newly created Code Block input and vary the size of the radii based on the radius ratio.
The simplest geometrical object in the Dynamo standard geometry library is a point. All geometry is created using special functions called constructors, which each return a new instance of that particular geometry type. In Dynamo, constructors begin with the name of the object’s type, in this case Point, followed by the method of construction. To create a three dimensional point specified by x, y, and z Cartesian coordinates, use the ByCoordinates constructor:
Constructors in Dynamo are typically designated with the “By” prefix, and invoking these functions returns a newly created object of that type. This newly created object is stored in the variable named on the left side of the equal sign.
Most objects have many different constructors, and we can use the BySphericalCoordinates constructor to create a point lying on a sphere, specified by the sphere’s radius, a first rotation angle, and a second rotation angle (specified in degrees):
Points can be used to construct higher dimensional geometry such as lines. We can use the ByStartPointEndPoint constructor to create a Line object between two points:
Similarly, lines can be used to create higher dimensional surface geometry, for instance using the Loft constructor, which takes a series of lines or curves and interpolates a surface between them.
Surfaces too can be used to create higher dimensional solid geometry, for instance by thickening the surface by a specified distance. Many objects have functions attached to them, called methods, allowing the programmer to perform commands on that particular object. Methods common to all pieces of geometry include Translate and Rotate, which respectively translate (move) and rotate the geometry by a specified amount. Surfaces have a Thicken method, which take a single input, a number specifying the new thickness of the surface.
Intersection commands can extract lower dimensional geometry from higher dimensional objects. This extracted lower dimensional geometry can form the basis for higher dimensional geometry, in a cyclic process of geometrical creation, extraction, and recreation. In this example, we use the generated Solid to create a Surface, and use the Surface to create a Curve.
Get the boundary geometry of all Catchments in a drawing.
Mission accomplished!
Check out the , or the Samples in the Forma Extension to get started. These will guide you through:
Download Dynamo 2.18.0 or higher from . For the best experience, choose the latest of the most stable versions, listed at the top.
Extract Dynamo using to a folder of your choice.
Easy setup
Multi-step installation process
No need to install Dynamo or have it open
Have to have Dynamo open
Does not recognize a graph that's already open in Dynamo
Open a graph in Player that's open in Dynamo with one button click
Can't use Python
Can use Python
Can only use sanctioned packages
Can use any package
In this section, you will find a series of lessons on the creation of geometry with DesignScript. Follow along by copying the example DesignScript into Dynamo Code Blocks.
You may have noticed a common theme in the names of nodes in Dynamo: each node uses a "." syntax without spaces. This is because the text at the top of each node represents the actual syntax for scripting, and the "." (or dot notation) separates an element from the possible methods we can call. This creates an easy translation from visual scripting to text-based scripting.
As a general analogy for the dot notation, how can we deal with a parametric apple in Dynamo? Below are a few methods we'll run on the apple before deciding to eat it. (Note: these are not actual Dynamo methods):
What color is the apple?
Apple.color
red
Is the apple ripe?
Apple.isRipe
true
How much does the apple weigh?
Apple.weight
6 oz.
Where did the apple come from?
Apple.parent
tree
What does the apple create?
Apple.children
seeds
Is this apple locally grown?
Apple.distanceFromOrchard
60 mi.
I don't know about you, but judging by the outputs in the table above, this looks like one tasty apple. I think I'll Apple.eat() it.
With the apple analogy in mind, let's look at Point.ByCoordinates and show how we can create a point using the code block.
The code block syntax Point.ByCoordinates(0,10);
gives the same result as a Point.ByCoordinates node in Dynamo, except we're able to create a point using one node. This is more efficient than the connecting a separate node into "X" and "Y".
By using Point.ByCoordinates in the code block, we are specifying the inputs in the same order as the out-of-the-box node (X,Y).
You can call any regular node in the library through a Code Block as long as the node isn’t a special “UI” node: those with a special user interface feature. For instance, you can call Circle.ByCenterPointRadius, but it wouldn’t make much sense to call a Watch 3D node.
Regular nodes (most of your library), generally come in three types. You’ll find that the library is organized with these categories in mind. Methods, or nodes, of these three types are treated differently when invoked within a Code Block.
Create - Create (or construct) something
Action - Perform an action on something
Query - Get a property of something that already exists
The "Create" category will construct geometry from scratch. We input values in the code block from left-to-right. These inputs are in the same order as the inputs on the node from top-to-bottom.
Comparing the Line.ByStartPointEndPoint node and the corresponding syntax in the code block, we get the same results.
An action is something you do to an object of that type. Dynamo uses dot notation, common to many coding languages, to apply an action to a thing. Once you have the thing, type a dot then the name of the action. The action-type method’s input is placed in parentheses just like create-type methods, only you don’t have to specify the first input you see on the corresponding node. Instead, we specify the element upon which we are performing the action:
The Point.Add node is an action-type node, so the syntax works a little differently.
The inputs are (1) the point, and (2) the vector to add to it. In a Code Block, we've named the point (the thing) “pt”. To add a vector named *“vec” *to “pt”, we would write pt.Add(vec), or: thing, dot, action. The Add action only has one input, or all the inputs from the Point.Add node minus the first one. The first input for the Point.Add node is the point itself.
Query-type methods get a property of an object. Since the object itself is the input, you don’t have to specify any inputs. No parentheses required.
Lacing with nodes is somewhat different from lacing with code block. With nodes, the user right clicks on the node and selects the lacing option to perform. With code block, the user has much more control as to how the data is structured. The code block shorthand method uses replication guides to set how several one-dimensional lists should be paired. Numbers in angled brackets "<>" define the hierarchy of the resulting nested list: <1>,<2>,<3>, etc.
In this example, we use a shorthand to define two ranges (more on shorthand in the following section of this chapter). In short,
0..1;
is equivalent to{0,1}
and-3..-7
is equivalent to{-3,-4,-5,-6,-7}
. The result gives us lists of 2 x-values and 5 y-values. If we don’t use replication guides with these mismatched lists, we get a list of two points, which is the length of the shortest list. Using replication guides, we can find all of the possible combinations of 2 and 5 coordinates (or, a Cross Product).Using the syntax Point.ByCoordinates
(x_vals<1>,y_vals<2>);
we get two lists with five items in each list.Using the syntax Point.ByCoordinates
(x_vals<2>,y_vals<1>);
we get five lists with two items in each list.
With this notation, we can also specify which list will be dominant: 2 lists of 5 things or 5 lists of 2 things. In the example, changing the order of the replication guides makes the result a list of rows of points or a list of columns of points in a grid.
While the code block methods above may take some getting used to, there is a feature in Dynamo called "Node to Code" which will make the process easier. To use this feature, select an array of nodes in your Dynamo graph, right-click on the canvas and select "Node to Code". Dynamo condenses these nodes into a code block, with all of the inputs and outputs! Not only is this a great tool for learning code block, but it also allows you to work with a more efficient and parametric Dynamo graph. We'll conclude the exercise below by using "Node to Code", so don't miss it.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
To show the power of code block, we are going to translate an existing attractor field definition into code block form. Working with an existing definition demonstrates how code block relates to visual scripting, and is helpful for learning DesignScript syntax.
Begin by recreating the definition in the image above (or by opening the sample file).
Notice that the lacing on Point.ByCoordinates has been set to Cross Product.
Each point in a grid is moved up in the Z direction based on its distance to the reference point.
A surface is recreated and thickened, creating a bulge in the geometry relative to the distance to the reference point.
Starting from the beginning, let's define the reference point first: Point.ByCoordinates
(x,y,0);
We use the same Point.ByCoordinates syntax as is specified on the top of the reference point node.The variables x and y are inserted into the Code Block so that we may update these dynamically with sliders.
Add some sliders to the Code Block inputs which range from -50 to 50. This way, we can span across the default Dynamo grid.
In the second line of the Code Block, we define a shorthand to replace the number sequence node:
coordsXY = (-50..50..#11);
We'll discuss this more in the next section. For now, notice that this shorthand is equivalent to the Number Sequence node in the visual script.
Now, we want to create a grid of points from the coordsXY sequence. To do this, we want to use the Point.ByCoordinates syntax, but also need to initiate a Cross Product of the list in the same manner that we did in the visual script. To do this, we type the line:
gridPts = Point.ByCoordinates(coordsXY<1>,coordsXY<2>,0);
The angled brackets denote the cross product reference.Notice in the Watch3D node that we have a grid of points across the Dynamo grid.
Now for the tricky part: We want to move the grid of points up based on their distance to the reference point. First, let's call this new set of points transPts. And since a translation is an action on an existing element, rather than using
Geometry.Translate...
, we usegridPts.Translate
Reading from the actual node on the canvas, we see that there are three inputs. The geometry to translate is already declared because we are performing the action on that element (with gridPts.Translate). The remaining two inputs will be inserted into the parentheses of the function: direction and distance.
The direction is simple enough, we use a
Vector.ZAxis()
to move vertically.The distance between the reference point and each grid point still needs to be calculated, so we do this as an action to the reference point in the same manner:
refPt.DistanceTo(gridPts)
The final line of code gives us the translated points:
transPts=gridPts.Translate(Vector.ZAxis(),refPt.DistanceTo(gridPts));
We now have a grid of points with the appropriate data structure to create a Nurbs Surface. We construct the surface using
srf = NurbsSurface.ByControlPoints(transPts);
And finally, to add some depth to the surface, we construct a solid using
solid = srf.Thicken(5);
In this case we thickened the surface by 5 units in the code, but we could always declare this as a variable (calling it thickness for example) and then control that value with a slider.
The "Node to Code" feature automates the entire exercise that we just completed with the click of a button. Not only is this powerful for creating custom definitions and reusable code blocks, but it is also a really helpful tool to learn how to script in Dynamo:
Start with the existing visual script from step 1 of the exercise. Select all of the nodes, right click on the canvas, and select "Node to Code". Simple as that.
Dynamo has automated a text based version of the visual graph, lacing and all. Test this out on your visual scripts and release the power of the code block!
Code blocks are a window deep into DesignScript, the programming language at the heart of Dynamo. Built from scratch to support exploratory design workflows, DesignScript is a readable and concise language that offers both immediate feedback to small bits of code and also scales to large and complex interactions. DesignScript also forms the backbone of the engine that drives most aspects of Dynamo “under the hood”. Because nearly all of the functionality found in Dynamo nodes and interactions have a one-to-one relationship with the scripting language, there are unique opportunities to move between node-based interactions and scripting in a fluid way.
For beginners, nodes can be automatically converted to text syntax to aid in learning DesignScript or simply to reduce the size of larger sections of graphs. This is done using a process called "Node to Code", which is outlined in more detail in the DesignScript Syntax section. More experienced users can use Code Blocks to create customized mashups of existing functionality and user authored relationships using many standard coding paradigms. In between the beginner and advanced user, there are a huge number of shortcuts and code snippets that will accelerate your designs. While the term 'code block' may be a little intimidating to non-programmers, it is both easy to use and robust. A beginner can use the code block efficiently with minimal coding, and an advanced user can define scripted definitions to be recalled elsewhere in a Dynamo definition.
In short, code blocks are a text-scripting interface within a visual-scripting environment. They can be used as numbers, strings, formulas, and other data types. The code block is designed for Dynamo, so one can define arbitrary variables in the code block, and those variables are automatically added to the inputs of the node:
With code blocks, a user has the flexibility to decide how to specify inputs. Here are several different ways to make a basic point with coordinates (10, 5, 0):
As you learn more of the available functions in the library, you might even find that typing “Point.ByCoordinates” is faster than searching in the library and finding the proper node. When you type in "Point." for example, Dynamo will display a list of possible functions to apply to a Point. This makes the scripting more intuitive and will help with learning how to apply functions in Dynamo.
The code block can be found in Core>Input>Actions>Code Block. But even faster, just double click on the canvas and the code block appears. This node is used so often, it's given full double-click privileges.
Code blocks are also flexible towards data types. The user can quickly define numbers, strings, and formulas and the code block will deliver the desired output.
In the image below, you can see the "old school" way of doing things is a little long-winded: the user searches for the intended node in the interface, adds the node to the canvas, and then inputs the data. With code block, the user can double-click on the canvas to pull up the node, and type in the correct data type with basic syntax.
The number and string nodes are two examples of Dynamo nodes which are arguably obsolete in comparison to the code block.
"Old-school"
Code Blocks
There are a few basic shorthand methods in the code block which, simply put, make data management a lot easier. We'll break down the basics below and discuss how this shorthand can be used both for creating and querying data.
Data Type
Standard Dynamo
Code Block Equivalent
Numbers
Strings
Sequences
Ranges
Get Item at Index
Create List
Concatenate Strings
Conditional Statements
Node(s)
Code Block Equivalent
Note
Any operator (+, &&, >=, Not, etc.)
+, &&, >=, !, etc.
Note that “Not” becomes “!” but the node is called “Not” to distinguish from “Factorial”
Boolean True
true;
Note lower case
Boolean False
false;
Note lower case
The method for defining ranges and sequences can be reduced to basic shorthand. Use the image below as a guide to the ".." syntax for defining a list of numerical data with code block. After getting the hang of this notation, creating numerical data is a really efficient process:
In this example, a number range is replaced by basic Code Block syntax defining the
beginning..end..step-size;
. Represented numerically, we get:0..10..1;
Notice that the syntax
0..10..1;
is equivalent to0..10;
A step-size of 1 is the default value for the shorthand notation. So0..10;
will give a sequence from 0 to 10 with a step-size of 1.The Sequence example is similar, except we use a "#" to state that we want 15 values in the list, rather than a list which goes up to 15. In this case, we are defining:
beginning..#ofSteps..step-size:
The actual syntax for the sequence is0..#15..2
Using the "#" from the previous step, we now place it in the "step-size" portion of the syntax. Now, we have a number range spanning from the "beginning" to the "end" and the "step-size" notation evenly distributes a number of values between the two:
beginning..end..#ofSteps
Creating advanced ranges allows us to work with list of lists in a simple fashion. In the examples below, we're isolating a variable from the primary range notation, and creating another range of that list.
1. Creating nested ranges, compare the notation with a "#" vs. the notation without. The same logic applies as in basic ranges, except it gets a little more complex.
2. We can define a sub-range at any place within the primary range, and notice that we can have two sub-ranges as well.
3. By controlling the "end" value in a range, we create more ranges of differing lengths.
As a logic exercise, compare the two shorthands above and try to parse through how subranges and the # notation drive the resultant output.
In addition to making lists with shorthand, we can also create lists on the fly. These list can contain a wide range of element types and can also be queried (remember, lists are objects in themselves). To summarize, with code block you make lists and query items from a list with brackets (a.k.a. “square brackets”):
1. Create lists quickly with strings and query them using the item index.
2. Create lists with variables and query using the range shorthand notation.
And managing with nested lists is a similar process. Be aware of the list order and recall using multiple sets of square brackets:
1. Define a list of lists.
2. Query a list with single bracket notation.
3. Query an item with double bracket notation.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this exercise, we will flex our new shorthand skills to create a funky-cool eggshell surface defined by ranges and formulas. During this exercise, notice how we use code block and existing Dynamo nodes in tandem: we use the code block for the heavy data lifting while the Dynamo nodes are visually laid out for legibility of the definition.
Start by creating a surface by connecting the nodes above. Instead of using a number node to define width and length, double click on the canvas and type 100;
into a code block
Define a range between 0 and 1 with 50 divisions by typing
0..1..#50
into a Code Block.Connect the range into Surface.PointAtParameter, which takes u and v values between 0 and 1 across the surface. Remember to change the Lacing to Cross Product by right clicking on the Surface.PointAtParameter node.
In this step, we employ our first function to move the grid of points up in the Z. This grid will drive a generated surface based on the underlying function. Add new nodes as shown in image below
We use a Code Block with the line:
(0..Math.Sin(x*360)..#50)*5;
. To quickly break this down, we're defining a range with a formula inside of it. This formula is the Sine function. The sine function receives degree inputs in Dynamo, so in order to get a full sine wave, we multiple our x values (this is the range input from 0 to 1) by 360. Next we want the same number of divisions as control grid points for each row, so we define fifty subdivisions with #50. Finally, the multiplier of 5 simply increases the amplitude of translation so that we can see the effect in the Dynamo Preview.
While the previous Code Block worked fine, it wasn't completely parametric. We want to dynamically drive its parameters, so we'll replace the line from the previous step with
(0..Math.Sin(x*360*cycles)..#List.Count(x))*amp;
. This gives us the ability to define these values based on inputs.
By changing the sliders (ranging from 0 to 10), we get some interesting results.
By doing a transpose on the number range, we reverse the direction of the curtain wave:
transposeList = List.Transpose(sineList);
We get a distorted eggshell surface when we add the sineList and the transposeList:
eggShellList = sineList+transposeList;
Let's change the sliders values specified below to 'calm the waters' of this algorithm.
Last, let's query isolated parts of the data with the Code Block. To regenerate the surface with a specific range of points, add the code block above between the Geometry.Translate and NurbsSurface.ByPoints node. This has the line of text: sineStrips[0..15..1];
. This will select the first 16 rows of points (out of 50). Recreating the surface, we can see that we've generated an isolated portion of the grid of points.
In the final step, to make this Code Block more parametric, we drive the query by using a slider ranging from 0 to 1. We do this with this line of code:
sineStrips[0..((List.Count(sineStrips)-1)*u)];
. This may seem confusing, but the line of code gives us a quick way to scale the length of the list into a multiplier between 0 and 1.
A value of 0.53
on the slider creates a surface just past the midpoint of the grid.
And as expected, a slider of 1
creates a surface from the full grid of points.
Looking at the visual graph, we can highlight the code blocks and see each of their functions.
1. The first Code Block replaces the Number node.
2. The second Code Block replaces the Number Range node.
3. The third Code Block replaces the List.Transpose, List.Count and Number Range nodes.
4. The fourth Code Block queries a list of lists, replacing the List.GetItemAtIndex node.
Certain geometry objects can be created by explicitly stating x, y, and z coordinates in three-dimensional space. More often, however, geometry is moved into its final position using geometric transformations on the object itself or on its underlying CoordinateSystem.
The simplest geometric transformation is a translation, which moves an object a specified number of units in the x, y, and z directions.
While all objects in Dynamo can be translated by appending the .Translate method to the end of the object’s name, more complex transformations require transforming the object from one underlying CoordinateSystem to a new CoordinateSystem. For instance, to rotate an object 45 degrees around the x axis, we would transform the object from its existing CoordinateSystem with no rotation, to a CoordinateSystem which had been rotated 45 degrees around the x axis with the .Transform method:
In addition to being translated and rotated, CoordinateSystems can also be created scaled or sheared. A CoordinateSystem can be scaled with the .Scale method:
Sheared CoordinateSystems are created by inputting non-orthogonal vectors into the CoordinateSystem constructor.
Scaling and shearing are comparatively more complex geometric transformations than rotation and translation, so not every Dynamo object can undergo these transformations. The following table outlines which Dynamo objects can have non-uniformly scaled CoordinateSystems, and sheared CoordinateSystems.
Arc
No
No
NurbsCurve
Yes
Yes
NurbsSurface
No
No
Circle
No
No
Line
Yes
Yes
Plane
No
No
Point
Yes
Yes
Polygon
No
No
Solid
No
No
Surface
No
No
Text
No
No
While Dynamo is capable of creating a variety of complex geometric forms, simple geometric primitives form the backbone of any computational design: either directly expressed in the final designed form, or used as scaffolding off of which more complex geometry is generated.
While not strictly a piece of geometry, the CoordinateSystem is an important tool for constructing geometry. A CoordinateSystem object keeps track of both position and geometric transformations such as rotation, sheer, and scaling.
Creating a CoordinateSystem centered at a point with x = 0, y = 0, z = 0, with no rotations, scaling, or sheering transformations, simply requires calling the Identity constructor:
CoordinateSystems with geometric transformations are beyond the scope of this chapter, though another constructor allows you to create a coordinate system at a specific point, CoordinateSystem.ByOriginVectors:
The simplest geometric primitive is a Point, representing a zero-dimensional location in three-dimensional space. As mentioned earlier there are several different ways to create a point in a particular coordinate system: Point.ByCoordinates creates a point with specified x, y, and z coordinates; Point.ByCartesianCoordinates creates a point with a specified x, y, and z coordinates in a specific coordinate system; Point.ByCylindricalCoordinates creates a point lying on a cylinder with radius, rotation angle, and height; and Point.BySphericalCoordinates creates a point lying on a sphere with radius and two rotation angle.
This example shows points created at various coordinate systems:
The next higher dimensional Dynamo primitive is a line segment, representing an infinite number of points between two end points. Lines can be created by explicitly stating the two boundary points with the constructor Line.ByStartPointEndPoint, or by specifying a start point, direction, and length in that direction, Line.ByStartPointDirectionLength.
Dynamo has objects representing the most basic types of geometric primitives in three dimensions: Cuboids, created with Cuboid.ByLengths; Cones, created with Cone.ByPointsRadius and Cone.ByPointsRadii; Cylinders, created with Cylinder.ByRadiusHeight; and Spheres, created with Sphere.ByCenterPointRadius.
In computational designs, curves and surfaces are frequently used as the underlying scaffold to construct subsequent geometry. In order for this early geometry to be used as a foundation for later geometry, the script must be able to extract qualities such as position and orientation across the entire area of the object. Both curves and surfaces support this extraction, and it is called parameterization.
All of the points on a curve can be thought of as having a unique parameter ranging from 0 to 1. If we were to create a NurbsCurve based off of several control or interpolated points, the first point would have the parameter 0, and the last point would have the parameter 1. It’s impossible to know in advance what the exact parameter is any intermediate point is, which may sound like a severe limitation though is mitigated by a series of utility functions. Surfaces have a similar parameterization as curves, though with two parameters instead of one, called u and v. If we were to create a surface with the following points:
p1 would have parameter u = 0 v = 0, while p9 would have parameters u = 1 v = 1.
Parameterization isn’t particularly useful when determining points used to generate curves, its main use is to determine the locations if intermediate points generated by NurbsCurve and NurbsSurface constructors.
Curves have a method PointAtParameter, which takes a single double argument between 0 and 1, and returns the Point object at that parameter. For instance, this script finds the Points at parameters 0, .1, .2, .3, .4, .5, .6, .7, .8, .9, and 1:
Similarly, Surfaces have a method PointAtParameter which takes two arguments, the u and v parameter of the generated Point.
While extracting individual points on a curve and surface can be useful, scripts often require knowing the particular geometric characteristics at a parameter, such as what direction the Curve or Surface is facing. The method CoordinateSystemAtParameter finds not only the position but an oriented CoordinateSystem at the parameter of a Curve or Surface. For instance, the following script extracts oriented CoordinateSystems along a revolved Surface, and uses the orientation of the CoordinateSystems to generate lines which are sticking off normal to the surface:
As mentioned earlier, parameterization is not always uniform across the length of a Curve or a Surface, meaning that the parameter 0.5 doesn’t always correspond to the midpoint, and 0.25 doesn’t always correspond to the point one quarter along a curve or surface. To get around this limitation, Curves have an additional set of parameterization commands which allow you to find a point at specific lengths along a Curve.
Many of the examples so far have focused on the construction of higher dimensional geometry from lower dimensional objects. Intersection methods allow this higher dimensional geometry to generate lower dimensional objects, while the trim and select trim commands allow script to heavily modify geometric forms after they’ve been created.
The Intersect method is defined on all pieces of geometry in Dynamo, meaning that in theory any piece of geometry can be intersected with any other piece of geometry. Naturally some intersections are meaningless, such as intersections involving Points, as the resulting object will always be the input Point itself. The other possible combinations of intersections between objects are outlined in the following chart. The following chart outlines the result of various intersection operations:
The following very simple example demonstrates the intersection of a plane with a NurbsSurface. The intersection generates a NurbsCurve array, which can be used like any other NurbsCurve.
The Trim method is very similar to the Intersect method, in that it is defined for almost every piece of geometry. However, there are far more limitations on Trim than on Intersect.
Something to note about Trim methods is the requirement of a “select” point, a point which determines which geometry to discard, and which pieces to keep. Dynamo finds and discards the trimmed geometry closest to the select point.
Now that we've demonstrated how to use Python scripts in Dynamo, let's take a look at connecting Revit libraries into the scripting environment. Remember, we imported Python Standard and our Dynamo core nodes with the first four lines in the block of code below. To import the Revit nodes, Revit elements, and the Revit document manager, we only have to add a few more lines:
This gives us access to the Revit API and offers custom scripting for any Revit task. By combining the process of visual programming with Revit API scripting, collaboration and tool development improve significantly. For example, a BIM manager and a schematic designer can work together on the same graph. In this collaboration, they can improve design and execution of the model.
Create a new Revit Project.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In these exercises, we'll explore elementary Python scripts in Dynamo for Revit. The exercise will focus on dealing with Revit files and elements, as well as the communication between Revit and Dynamo.
This is a cut and dry method for retrieving the doc, uiapp, and app of the Revit file linked to your Dynamo session. Programmers who have worked in the Revit API before may notice the items in the watch list. If these items do not look familiar, that's okay; we'll be using other examples in the exercises below.
Here is how we're importing Revit Services and retrieving the document data in Dynamo.
Take a look at the Python node in Dynamo. You can also find the code from below:
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this exercise, we'll make a simple Model Curve in Revit using the Dynamo Python node.
Begin by creating a new Conceptual Mass family in Revit.
Open the Conceptual Mass Folder and use the Metric Mass.rft template file.
In Revit, use the keyboard shortcut un
to bring up the Project Unit settings, change the length unit to meters.
Launch Dynamo and create the set of nodes in the image below. We'll first create two reference points in Revit from Dynamo nodes.
Create a Code Block and give it a value of
"0;"
Plug this value into a ReferencePoint.ByCoordinates node for X,Y, and Z inputs.
Create three sliders, ranging from -100 to 100 with a step size of 1.
Connect each slider to a ReferencePoint.ByCoordinates node.
Add a Python node to the workspace, click the "+" button on the node to add another input and plug the two references points into each input. Open the Python node.
Take a look at the Python node in Dynamo. Find the full code at the below.
System.Array: Revit needs a System Array as an input (rather than a Python list). This is just one more line of code, but paying attention to argument types will facilitate Python programming in Revit.
In Dynamo, we've created two reference points with a line connecting them using Python. Let's take this a little further in the next exercise.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This exercise keeps it simple, but drives home the topics of connecting data and geometry from Revit to Dynamo and back. Let's begin by opening Revit-StructuralFraming.rvt. Once opened, launch Dynamo and open the file Revit-StructuralFraming.dyn.
This Revit file is about as basic as it gets. Two reference curves: one drawn on Level 1 and the other drawn on Level 2. We want to get these curves into Dynamo and maintain a live link.
In this file we have a set of nodes plugging into five inputs of a Python node.
Select Model Element Nodes: Hit the select button for each and select a corresponding curve in Revit.
Code Block: using the syntax
0..1..#x;
, connect an integer slider ranging from 0 to 20 into the x input. This designates the number of beams to draw between the two curves.Structural Framing Types: We'll choose the default W12x26 beam here from the dropdown menu.
Levels: select "Level 1".
This code in Python is a little more dense, but the comments within the code describe what's happening in the process
In Revit, we have an array of beams spanning the two curves as structural elements. Note: this isn't a realistic example...the structural elements are used as an example for native Revit instances created from Dynamo.
In Dynamo, we can see the results as well. The beams in the Watch3D node refer to the geometry queried from the Revit elements.
Notice that we have a continuous process of translating data from the Revit Environment to the Dynamo Environment. In summary, here's how the process plays out:
Select Revit element
Convert Revit element to Dynamo Curve
Divide Dynamo curve into a series of Dynamo points
Use the Dynamo points between two curves to create Dynamo lines
Create Revit beams by referencing Dynamo lines
Output Dynamo surfaces by querying the geometry of Revit beams
This may sound a little heavy handed, but the script makes it as simple as editing the curve in Revit and re-running the solver (although you may have to delete the previous beams when doing so). This is due to the fact that we are placing beams in python, thus breaking the association that OOTB nodes have.
With an update to the reference curves in Revit, we get a new array of beams.
Python is a widely-used programming language whose popularity has a lot to do with its style of syntax. It's highly readable, which makes it easier to learn than many other languages. Python supports modules and packages and can be embedded into existing applications. For information about how to get up and running with Python, a good resource is the page on
The plan behind the Dynamo Project is to widen the scope of platform implementation. As Dynamo adds more programs to the docket, users will gain access to platform-specific APIs from the Python scripting environment. While Revit is the case study for this section, we can anticipate more chapters in the future which offer comprehensive tutorials on scripting in other platforms. Additionally, there are many libraries accessible now which can be imported into Dynamo!
The examples below demonstrate ways to implement Revit-specific operations from Dynamo using Python. For a more detailed review on Python's relationship to Dynamo and Revit, refer to the . Another useful resource for Python and Revit is the Project.
With:
Surface
Curve
Plane
Solid
Surface
Curve
Point
Point, Curve
Surface
Curve
Point
Point
Point
Curve
Plane
Curve
Point
Curve
Curve
Solid
Surface
Curve
Curve
Solid
Using: Point
Curve
Plane
Surface
Solid
On: Curve
Yes
No
No
No
No
Polygon
-
No
Yes
No
No
Surface
-
Yes
Yes
Yes
Yes
Solid
-
-
Yes
Yes
Yes
Objects in computational designs are rarely created explicitly in their final position and form, and are most often translated,rotated, and otherwise positioned based off of existing geometry. Vector math serves as a kind-of geometric scaffolding to give direction and orientation to geometry, as well as to conceptualize movements through 3D space without visual representation.
At its most basic, a vector represents a position in 3D space, and is often times thought of as the endpoint of an arrow from the position (0, 0, 0) to that position. Vectors can be created with the ByCoordinates constructor, taking the x, y, and z position of the newly created Vector object. Note that Vector objects are not geometric objects, and don’t appear in the Dynamo window. However, information about a newly created or modified vector can be printed in the console window:
A set of mathematical operations are defined on Vector objects, allowing you to add, subtract, multiply, and otherwise move objects in 3D space as you would move real numbers in 1D space on a number line.
Vector addition is defined as the sum of the components of two vectors, and can be thought of as the resulting vector if the two component vector arrows are placed “tip to tail.” Vector addition is performed with the Add method, and is represented by the diagram on the left.
Similarly, two Vector objects can be subtracted from each other with the Subtract method. Vector subtraction can be thought of as the direction from first vector to the second vector.
Vector multiplication can be thought of as moving the endpoint of a vector in its own direction by a given scale factor.
Often it’s desired when scaling a vector to have the resulting vector’s length exactly equal to the scaled amount. This is easily achieved by first normalizing a vector, in other words setting the vector’s length exactly equal to one.
c still points in the same direction as a (1, 2, 3), though now it has length exactly equal to 5.
Two additional methods exist in vector math which don’t have clear parallels with 1D math, the cross product and dot product. The cross product is a means of generating a Vector which is orthogonal (at 90 degrees to) to two existing Vectors. For example, the cross product of the x and y axes is the z axis, though the two input Vectors don’t need to be orthogonal to each other. A cross product vector is calculated with the Cross method.
An additional, though somewhat more advanced function of vector math is the dot product. The dot product between two vectors is a real number (not a Vector object) that relates to, but is not exactly, the angle between two vectors. One useful properties of the dot product is that the dot product between two vectors will be 0 if and only if they are perpendicular. The dot product is calculated with the Dot method.
The two-dimensional analog to a NurbsCurve is the NurbsSurface, and like the freeform NurbsCurve, NurbsSurfaces can be constructed with two basic methods: inputting a set of base points and having Dynamo interpolate between them, and explicitly specifying the control points of the surface. Also like freeform curves, interpolated surfaces are useful when a designer knows precisely the shape a surface needs to take, or if a design requires the surface to pass through constraint points. On the other hand, Surfaces created by control points can be more useful for exploratory designs across various smoothing levels.
To create an interpolated surface, simply generate a two-dimensional collection of points approximating the shape of a surface. The collection must be rectangular, that is, not jagged. The method NurbsSurface.ByPoints constructs a surface from these points.
Freeform NurbsSurfaces can also be created by specifying underlying control points of a surface. Like NurbsCurves, the control points can be thought of as representing a quadrilateral mesh with straight segments, which, depending on the degree of the surface, is smoothed into the final surface form. To create a NurbsSurface by control points, include two additional parameters to NurbsSurface.ByPoints, indicating the degrees of the underlying curves in both directions of the surface.
We can increase the degree of the NurbsSurface to change the resulting surface geometry:
Just as Surfaces can be created by interpolating between a set of input points, they can be created by interpolating between a set of base curves. This is called lofting. A lofted curve is created using the Surface.ByLoft constructor, with a collection of input curves as the only parameter.
Surfaces of revolution are an additional type of surface created by sweeping a base curve around a central axis. If interpolated surfaces are the two-dimensional analog to interpolated curves, then surfaces of revolution are the two-dimensional analog to circles and arcs.
Surfaces of revolution are specified by a base curve, representing the “edge” of the surface; an axis origin, the base point of the surface; an axis direction, the central “core” direction; a sweep start angle; and a sweep end angle. These are used as the input to the Surface.Revolve constructor.
With Dynamo 2.0 we have the ability to specify a default template (.py extension)
to use when opening the python window for the first time. This has been a long-desired request as this expedites the usage of Python within Dynamo. Having the ability to use a template allows us to have default imports ready to go when we want to develop a custom Python script.
The location for this template is in the APPDATA
location for your Dynamo install.
This is typically as follows ( %appdata%\Dynamo\Dynamo Core\{version}\ )
.
In order to utilize this functionality we need to add the following line in our DynamoSettings.xml
file. (Edit in notepad)
Where we see <PythonTemplateFilePath />
, we can simply replace this with the following:
Note: replace CURRENTUSER with your username
Next we need to build a template with the functionality that we want to use built-in. In our case lets embed the Revit related imports and some of the other typical items when working with Revit.
You can start a blank notepad document and paste the following code inside:
Once that is done, save this file as PythonTemplate.py
in the APPDATA
location.
A_f_ter the python template is defined, Dynamo will look for this each time a Python node is placed. If it is not found it will look like the default Python window.
If the Python template is found (like our Revit one for example) you will see all of the default items you built in.
Additional information regarding this great addition (by Radu Gidei) can be found here. https://github.com/DynamoDS/Dynamo/pull/8122
From its origins as an add-on for Building Information Modeling in Revit, Dynamo has matured to become many things. Above all else it is a platform, enabling designers to explore visual programming, solve problems, and make their own tools. Let's start our journey with Dynamo by setting some context - what is it and how do I approach using it?
Not only does Dynamo 2.0 introduce the nodes previously discussed for dictionaries, there is new functionality in code blocks for this as well!
You can use syntax like below or DesignScript-based representations of the nodes.
Since a dictionary is an object type in Dynamo we can commit the following actions upon it.
Maintaining these sort of interactions becomes especially useful when relating Revit data to strings. Next, we will look at some Revit use-cases.
Let's add one more tier to the hierarchy. If we take the deck of cards from the original example and create a box which contains multiple decks, the box now represents a list of decks, and each deck represents a list of cards. This is a list of lists. For the analogy in this section, the image below contains a list of coin rolls, and each roll contains a list of pennies.
Photo by Dori.
What queries can we make from the list of lists? This accesses existing properties.
Number of coin types? 2.
Coin type values? $0.01 and $0.25.
Material of quarters? 75% copper and 25% nickel.
Material of pennies? 97.5% zinc and 2.5% copper.
What actions can we perform on the list of lists? This changes the list of lists based on a given operation.
Select a specific stack of quarters or pennies.
Select a specific quarter or penny.
Rearrange the stacks of quarters and pennies.
Shuffle the stacks together.
Again, Dynamo has an analogous node for each one of the operations above. Since we're working with abstract data and not physical objects, we need a set of rules to govern how we move up and down the data hierarchy.
When dealing with lists of lists, the data is layered and complex, but this provides an opportunity to do some awesome parametric operations. Let's break down the fundamentals and discuss a few more operations in the lessons below.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
The fundamental concept to learn from this section: Dynamo treats lists as objects in and of themselves. This top-down hierarchy is developed with object-oriented programming in mind. Rather than selecting sub-elements with a command like List.GetItemAtIndex, Dynamo will select that index of the main list in the data structure. And that item can be another list. Let's break it down with an example image:
With Code Block, we've defined two ranges:
0..2; 0..3;
These ranges are connected to a Point.ByCoordinates node with lacing set to "Cross Product". This creates a grid of points, and also returns a list of lists as an output.
Notice that the Watch node gives 3 lists with 4 items in each list.
When using List.GetItemAtIndex, with an index of 0, Dynamo selects the first list and all of its contents. Other programs may select the first item of every list in the data structure, but Dynamo employs a top-down hierarchy when dealing with data.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Flatten removes all tiers of data from a data structure. This is helpful when the data hierarchies are not necessary for your operation, but it can be risky because it removes information. The example below shows the result of flattening a list of data.
Insert one line of code to define a range in Code Block:
-250..-150..#4;
Plugging the code block into the x and y input of a Point.ByCoordinates node, we set the lacing to "Cross Product" to get a grid of points.
The Watch node shows that we have a list of lists.
A PolyCurve.ByPoints node will reference each list and create a respective polycurve. Notice in the Dynamo preview that we have four polycurves representing each row in the grid.
By inserting a flatten before the polycurve node, we've created one single list for all of the points. The PolyCurve.ByPoints node references a list to create one curve, and since all of the points are on one list, we get one zig-zag polycurve which runs throughout the entire list of points.
There are also options for flattening isolated tiers of data. Using the List.Flatten node, you can define a set number of data tiers to flatten from the top of the hierarchy. This is a really helpful tool if you're struggling with complex data structures which are not necessarily relevant to your workflow. And another option is to use the flatten node as a function in List.Map. We'll discuss more about List.Map below.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
When parametric modeling, there are also times where you'll want to modify the data structure to an existing list. There are many nodes available for this as well, and chop is the most basic version. With chop, we can partition a list into sublists with a set number of items.
The chop command divides lists based on a given list length. In some ways, chop is the opposite of flatten: rather than removing data structure, it adds new tiers to it. This is a helpful tool for geometric operations like the example below.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
A List.Map/Combine applies a set function to an input list, but one step down in the hierarchy. Combinations are the same as Maps, except combinations can have multiple inputs corresponding to the input of a given function.
Note: This exercise was created with a previous version of Dynamo. Much of the List.Map functionality has been resolved with the addition of the List@Level feature. For more information, see List@Level below.
As a quick introduction, let's review the List.Count node from a previous section.
The List.Count node counts all of the items in a list. We'll use this to demonstrate how List.Map works.
Insert two lines of code into the Code Block:
-50..50..#Nx; -50..50..#Ny;
After typing in this code, the code block will create two inputs for Nx and Ny.
With two integer sliders, define the Nx and Ny values by connecting them to the Code Block.
Connect each line of the code block into the respective X and Y inputs of a Point.ByCoordinates node. Right click the node, select "Lacing", and choose "Cross Product". This creates a grid of points. Because we defined the range from -50 to 50, we are spanning the default Dynamo grid.
A Watch node reveals the points created. Notice the data structure. We've created a list of lists. Each list represents a row of points of the grid.
Attach a List.Count node to the output of the watch node from the previous step.
Connect a Watch node to the List.Count output.
Notice that the List.Count node gives a value of 5. This is equal to the "Nx" variable as defined in the code block. Why is this?
First, the Point.ByCoordinates node uses the "x" input as the primary input for creating lists. When Nx is 5 and Ny is 3, we get a list of 5 lists, each with 3 items.
Since Dynamo treats lists as objects in and of themselves, a List.Count node is applied to the main list in the hierarchy. The result is a value of 5, or, the number of lists in the main list.
By using a List.Map node, we take a step down in the hierarchy and perform a "function" at this level.
Notice that the List.Count node has no input. It is being used as a function, so the List.Count node will be applied to every individual list one step down in the hierarchy. The blank input of List.Count corresponds to the list input of List.Map.
The results of List.Count now gives a list of 5 items, each with a value of 3. This represents the length of each sublist.
Note: This exercise was created with a previous version of Dynamo. Much of the List.Combine functionality has been resolved with the addition of the List@Level feature. For more information, see List@Level below.
In this exercise, we will use List.Combine to demonstrate how it can be used to apply a function across separate lists of objects.
Start by setting up two lists of points.
Use Sequence node to generate 10 values, each with a 10 step increment.
Connect the result to the x input of a Point.ByCoordinates node. This will create a list of points in Dynamo.
Add a second Point.ByCoordinates node to the workspace, use the same Sequence output as its x input, but use an Integer Slider as its y input, and set its value to 31 (it can be any value as long as they do not overlap with the first set of points) so the 2 sets of points are not overlapped on top of each other.
Next, we will use List.Combine to apply a function on objects in 2 separate lists. In this case, it will be a simple draw line function.
Add List.Combine to the workspace and connect the 2 set of points as its list0 & list1 input.
Use a Line.ByStartPointEndPoint as the input function for List.Combine.
Once completed, the 2 set of points are zipped/paired together through a Line.ByStartPointEndPoint function and returning 10 lines in Dynamo.
Refer to exercise in n-Dimensional Lists to see another example of using List.Combine.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Preferred to List.Map, the List@Level feature allows you to directly select which level of list you want to work with right at the input port of the node. This feature can be applied to any incoming input of a node and will allow you access the levels of your lists quicker and easier than other methods. Just tell the node what level of the list you want to use as the input and let the node do the rest.
In this exercise, we will use the List@Level feature to isolate a specific level of data.
We will start with a simple 3D grid of points.
The grid is constructed with a Range for X, Y and Z, we know that the data is structured with 3 tiers: an X List, Y List and Z List.
These tiers exist at different Levels. The Levels are indicated at the bottom of the Preview Bubble. The list Levels columns correspond to the list data above to help identify which level to work within.
The list levels are organized in reverse order so that the lowest level data is always in “L1”. This will help ensure that your graphs will work as planned, even if anything is changed upstream.
To use the List@Level function, click '>'. Inside this menu, you will see two checkboxes.
Use Levels - This enables the List@Level functionality. After clicking on this option, you will be able to click through and select the input list levels you want the node to use. With this menu, you can quickly try out different level options by clicking up or down.
Keep list structure – If enabled, you will have the option to keep that input’s level structure. Sometimes, you may have purposefully organized your data into sublists. By checking this option, you can keep your list organization intact and not lose any information.
With our simple 3D grid, we can access and visualize the list structure by toggling through the List Levels. Each list level and index combination will return a different set of points from our original 3D set.
“@L2” in DesignScript allows us to select only the List at Level 2. The List at Level 2 with the index 0 includes only the first set of Y points, returning only the XZ grid.
If we change the Level filter to “L1”, we will be able to see everything in the first List Level. The List at Level 1 with the index 0 includes all of our 3D points in a flat list.
If we try the same for “L3” we will see only the third List Level points. The List at Level 3 with the index 0 includes only the first set of Z points, returning only an XY grid.
If we try the same for “L4” we will see only the third List Level points. The List at Level 4 with the index 0 includes only the first set of X points, returning only an YZ grid.
Although this particular example can also be created with List.Map, List@Level greatly simplifies the interaction, making it easy to access the node data. Take a look below at a comparison between a List.Map and List@Level methods:
Although both methods will give us access to the same points, the List@Level method allows us to easily toggle between layers of data within a single node.
To access a point grid with List.Map, we will need a List.GetItemAtIndex node alongside the List.Map. For every list level that we are stepping down, we will need to use an additional List.Map node. Depending on the complexity of your lists, this could require you to add a significant amount of List.Map Nodes to your graph to access the right level of information.
In this example, a List.GetItemAtIndex node with a List.Map node returns the same set of points with the same list structure as the List.GetItemAtIndex with '@L3' selected.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Transpose is a fundamental function when dealing with lists of lists. Just as in spreadsheet programs, a transpose flips the columns and rows of a data structure. We'll demonstrate this with a basic matrix below, and in the following section, we'll demonstrate how a transpose can be use to create geometric relationships.
Let's delete the List.Count nodes from the previous exercise and move on to some geometry to see how the data structured.
Connect a PolyCurve.ByPoints to the output of the watch node from Point.ByCoordinates.
The output shows 5 polycurves, and we can see the curves in our Dynamo preview. The Dynamo node is looking for a list of points (or a list of lists of points in this case) and creating a single polycurve from them. Essentially, each list has converted to a curve in the data structure.
A List.Transpose node will switch all of the items with all of the lists in a list of lists. This sounds complicated, but it's the same logic as transpose in Microsoft Excel: switching columns with rows in a data structure.
Notice the abstract result: the transpose changed the list structure from a 5 lists with 3 items each to 3 lists with 5 items each.
Notice the geometric result: using PolyCurve.ByPoints, we get 3 polycurves in the perpendicular direction to the original curves.
Code block shorthand uses "[]" to define a list. This is a much faster and more fluid way to create list than the List.Create node. Code block is discussed in more detail in Code Blocks and DesignScript. Reference the image below to note how a list with multiple expressions can be defined with code block.
Code block shorthand uses "[]" as a quick and easy way to select specific items that you want from a complex data structure. Code blocks are discussed in more detail in Code Block and DesignScript chapter. Reference the image below to note how a list with multiple data types can be queried with code block.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
This exercise uses some of the logic established in the previous one to edit a surface. Our goal here is intuitive, but the data structure navigation will be more involved. We want to articulate a surface by moving a control point.
Begin with the string of nodes above. We are creating a basic surface which spans the default Dynamo grid.
Using Code Block, insert these two lines of code and connect to the u and v inputs of Surface.PointAtParameter, respectively:
-50..50..#3;
-50..50..#5;
Be sure to set the Lacing of Surface.PointAtParameter to "Cross Product".
The Watch node show that we have a list of 3 lists, each with 5 items.
In this step, we want to query the central point in the grid we've created. To do this we'll select the middle point in the middle list. Makes sense, right?
To confirm that this is the correct point, we can also click through the watch node items to confirm that we're targeting the correct one.
Using Code Block, we'll write a basic line of code for querying a list of lists:
points[1][2];
Using Geometry.Translate, we'll move the selected point up in the Z direction by 20 units.
Let's also select the middle row of points with a List.GetItemAtIndex node. Note: Similar to a previous step, we can also query the list with Code Block, using a line of
points[1];
So far we've successfully queried the center point and moved it upward. Now we want need to insert this moved point back into the original data structure.
First, we want to replace the item of the list we isolated in a previous step.
Using List.ReplaceItemAtIndex, we'll replace the middle item by using and index of "2", with the replacement item connected to the moved point (Geometry.Translate).
The output shows that we've input the moved point into the middle item of the list.
Now that we've modified the list, we need to insert this list back into the original data structure: the list of lists.
Following the same logic, use List.ReplaceItemAtIndex to replace the middle list with the our modified list.
Notice that the Code Blocks defining the index for these two nodes are 1 and 2, which matches the original query from the Code Block (points[1][2]).
By selecting the list at index 1, we see the data structure highlighted in the Dynamo preview. We successfully merged the moved point into the original data structure.
There are many ways to make a surface from this set of points. In this case, we're going to create a surface by lofting curves together.
Create a NurbsCurve.ByPoints node and connect the new data structure to create three nurbs curves.
Connect a Surface.ByLoft to the output from NurbsCurve.ByPoints. We now have a modified surface. We can change the original Z value of Geometry. Translate and watch the geometry update!
Custom Nodes are constructed by nesting other nodes and custom nodes inside of a "Dynamo Custom Node," which we can think of conceptually as a container. When this container node is executed in your graph, everything inside it will be executed to allow you to reuse and share a useful combination of nodes.
When you have multiple copies of a custom node in your graph, you can update all of them by editing the base custom node. This allows you to update your graph seamlessly by adapting to any changes that may occur in workflow or design.
Arguably the best feature of custom nodes is their work sharing capabilities. If a "power user" creates a complex Dynamo graph and hands it off to a designer who is new to Dynamo, he/she can condense the graph to the bare essentials for design interaction. The custom node can be opened to edit the internal graph, but the "container" can be kept simple. With this process, custom nodes allow Dynamo users to design a graph that is clean and intuitive.
Let's jump into the custom node environment and make a simple node to calculate a percentage. The custom node environment is different from the Dynamo graph environment, but the interaction is fundamentally the same. With that said, let's create our first custom node!
To create a Custom Node from scratch, Launch Dynamo and select Custom Node, or type Ctrl + Shift + N from the canvas.
Assign a name, description, and category in the Custom Node Properties dialog.
Name: Percentage
Description: Calculate the percentage of one value in relation to another.
Category: Math.Functions
This will open a canvas with a yellow background, indicating that you are working inside a custom node. In this canvas you have access to all of the core Dynamo nodes, as well as the Input and Output nodes, which label the data flowing into and out of the custom node. They can be found in Input>Basic.
Inputs: Input nodes create input ports on the custom node. The syntax for an input node is input_name : datatype = default_value(optional).
You can save this custom node as a .dyf (as opposed to the standard .dyn) file and it will automatically be added to your session and future sessions. You will find the custom node in your library from the Add-ons section.
Now that we've created our first custom node, the next sections will dive deeper into custom node functionality and how to publish generic workflows. In the following section, we'll look at developing a custom node that transfers geometry from one surface to another.
Have you ever wanted to look up something in Revit by a piece of data that it has?
Chances are if you have you've done something like the following example.
In the image below we are collecting all of the rooms in the Revit model, getting the index of the room we want (by room number), and finally grabbing the room at the index.
Collect all rooms in the model.
Room number to find.
Get the room number and find what index it is at.
Obtain the room at the index.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Now let's recreate this idea using dictionaries. First we need to collect all of the rooms in our Revit model.
We choose the Revit category we want to work with, (In this case, we are working with rooms).
We tell Dynamo to collect all of those elements
The data that we will use is the room number.
Now we will create the dictionary with the given keys and elements.
The node, Dictionary.ByKeysValues will create a dictionary given the appropriate inputs.
Keys
need to be a string, whilevalues
can be a variety of object types.
Lastly, we can retrieve a room from the dictionary with its room number now.
String
will be the key that we are using to look up an object from the dictionary.Dictionary.ValueAtKey will obtain the object from the dictionary now.
Using this same dictionary logic, we can create dictionaries with grouped objects as well. If we wanted to look up all rooms at a given level we can modify the above graph as follows.
Rather than using the room number as the key, we can now use a parameter value, (in this case we will use level).
Now, we can group the rooms by the level that they reside on.
With the elements grouped by the level, we can now use the shared keys (unique keys) as our key for our dictionary, and the lists of rooms as the elements.
Lastly, using the levels in the Revit model, we can look up which rooms reside on that level in the dictionary.
Dictionary.ValueAtKey
will take the level name and return the room objects at that level.
The opportunities for Dictionary use are really endless. The ability to relate your BIM data in Revit to the element itself poses a variety of use cases.
Dynamo offers several different methods for creating custom nodes. You can build custom nodes from scratch, from an existing graph, or explicitly in C#. In this section we will cover building a custom node in the Dynamo UI from an existing graph. This method is ideal for cleaning up the workspace, as well as packaging a sequence of nodes to reuse elsewhere.
In the image below, we map a point from one surface to another using UV coordinates. We'll use this concept to create a panelized surface which references curves in the XY plane. We'll create quad panels for our panelization here, but using the same logic, we can create a wide variety of panels with UV mapping. This is a great opportunity for custom node development because we will be able to repeat a similar process more easily in this graph or in other Dynamo workflows.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Code Block: Use this line to create a range of 10 numbers between -45 and 45
45..45..#10;
Point.ByCoordinates: Connect the output of the Code Block to the ‘x’ and ‘y’ inputs and set the lacing to cross-reference. You should now have a grid of points.
Plane.ByOriginNormal: Connect the ‘Point’ output to the ‘origin’ input to create a plane at each of the points. The default normal vector of (0,0,1) will be used.
Rectangle.ByWidthLength: Connect the planes from the previous step into the ‘plane’ input, and use a Code Block with a value of 10 to specify the width and length.
You should now see a grid of rectangles. Let’s map these rectangles to a target surface using UV coordinates.
Polygon.Points: Connect the Rectangle.ByWidthLength output from the previous step to the ‘polygon’ input to extract the corner points of each rectangle. These are the points that we will map to the target surface.
Rectangle.ByWidthLength: Use a Code Block with a value of 100 to specify the width and length of a rectangle. This will be the boundary of our base surface.
Surface.ByPatch: Connect the Rectangle.ByWidthLength from the previous step to the ‘closedCurve’ input to create a base surface.
Surface.UVParameterAtPoint: Connect the ‘Point’ output of the Polygon.Points node and the ‘Surface’ output of the Surface.ByPatch node to return the UV parameter at each point.
Now that we have a base surface and a set of UV coordinates, we can import a target surface and map the points between surfaces.
File Path: Select the file path of the surface you want to import. The file type should be .SAT. Click the "Browse..." button and navigate to the UVmapping_srf.sat file from the .zip file downloaded above.
Geometry.ImportFromSAT: Connect the file path to import the surface. You should see the imported surface in the geometry preview.
UV: Connect the UV parameter output to a UV.U and a UV.V node.
Surface.PointAtParameter: Connect the imported surface as well as the u and v coordinates. You should now see a grid of 3D points on the target surface.
The final step is to use the 3D points to construct rectangular surface patches.
PolyCurve.ByPoints: Connect the points on the surface to construct a polycurve through the points.
Boolean: Add a Boolean to the workspace and connect it to the ‘connectLastToFirst’ input and toggle to True to close the polycurves. You should now see rectangles mapped to the surface.
Surface.ByPatch: Connect the polycurves to the ‘closedCurve’ input to construct surface patches.
Now let’s select the nodes that we want to nest into a Custom Node, thinking about what we want to be the inputs and outputs of our node. We want our Custom Node to be as flexible as possible, so it should be able to map any polygons, not just rectangles.
Select the following Nodes (beginning with Polygon.Points), right click on the workspace and select ‘Create Custom Node’.
In the Custom Node Properties dialog, assign a name, description, and category to the Custom Node.
Name: MapPolygonsToSurface
Description: Map polygon(s) from a base to target surface
Add-ons Category: Geometry.Curve
The Custom Node has considerably cleaned up the workspace. Notice that the inputs and outputs have been named based on the original nodes. Let’s edit the Custom Node to make the names more descriptive.
Double click the Custom Node to edit it. This will open a workspace with a yellow background representing the inside of the node.
Inputs: Change the input names to baseSurface and targetSurface.
Outputs: Add an additional output for the mapped polygons.
Save the custom node and return to the home workspace. Notice the MapPolygonsToSurface node reflects the changes we just made.
We can also add to the robustness of the Custom Node by adding in Custom Comments. Comments can help to hint at the input and output types or explain the functionality of the node. Comments will appear when the user hovers over an input or output of a Custom Node.
Double click the Custom Node to edit it. This will re-open the yellow background workspace.
Begin editing the Input Code Block. To start a Comment, type "//" followed by the comment text. Type anything that may help to clarify the Node - Here we will describe the targetSurface.
Let's also set the default value for the inputSurface by setting the input type equal to a value. Here, we will set the default value to the original Surface.ByPatch set.
Comments can also be applied to the Outputs.
Edit the text in the Output Code Block. Type "//" followed by the comment text. Here we will clarify the Polygons and the surfacePatches Outputs by adding a more in-depth description.
Hover over the Custom Node Inputs to see the Comments.
With the default value set on our inputSurface, we can also run the definition without a surface input.
There are a wide variety of ways to build custom nodes in Dynamo. In the examples in this chapter, we'll create custom nodes directly from the Dynamo UI. If you are a programmer and you are interested in C# or Zero-Touch formatting, you can reference on the Dynamo Wiki for a more in-depth review.
Outputs: Similar to inputs, these will create and name output ports on the custom node. Consider adding a Custom Comment to your Input and Output ports to hint at the Input and Output types. This is discussed in more detail in the .
Next, we need to decide what keys we are going to use to look up this data by. (Information on keys can be found on the section, ).
Let’s start by creating a graph that we want to nest into a custom node. In this example, we will create a graph that maps polygons from a base surface to a target surface, using UV coordinates. This UV mapping process is something we use frequently, making it a good candidate for a custom node. For more information on surfaces and UV space, refer to the page. The complete graph is UVmapping_Custom-Node.dyn from the .zip file downloaded above.
Dynamo offers a variety of ways to create a package for your personal use or for sharing with the Dynamo community. In the case study below, we'll walk through how a package is set up by deconstructing an existing one. This case study builds on lessons from the previous chapter, providing a set of custom nodes for mapping geometry, by UV coordinates, from one Dynamo surface to another.
We're going to work with a sample package which demonstrates the UV mapping of points from one surface to another. We've already built the fundamentals of the tool in the Creating a Custom Node section of this primer. The files below demonstrate how we can take the concept of UV Mapping and develop a set of tools for a publishable library.
In this image, we map a point from one surface to another using UV coordinates. The package is based on this concept, but with more complex geometry.
In the previous chapter, we explored ways for panelizing a surface in Dynamo based on curves defined in the XY plane. This case study extends these concepts for more dimensions of geometry. We're going to install this package as built in order to demonstrate how it was developed. In the next section, we'll demonstrate how this package was published.
In Dynamo, click Packages > Package Manager and search for the package "MapToSurface" (all one word). Click Install to start the download and add the package to your library.
After installing, the custom nodes should be available under the Add-ons > DynamoPrimer section.
With the package now installed, let's walk through how it's set up.
The package we're creating uses five custom nodes that we've built for reference. Let's walk through what each node does below. Some custom nodes build off of other custom nodes, and the graphs have a layout for other users to understand in a straightforward manner.
This is a simple package with five custom nodes. In the steps below, we'll briefly talk about each custom node's setup.
This is a basic custom node, and one from which all of the other mapping nodes are based. Simply put, the node maps a point from a source surface UV coordinate to the location of the target surface UV coordinate. And since points are the most primitive geometry, from which more complex geometry is built, we can use this logic to map 2D, and even 3D geometry from one surface to another.
The logic of extending mapped points from 1D geometry to 2D geometry is demonstrated simply with polygons here. Notice that we have nested the "PointsToSurface" node into this custom node. This way we can map the points of each polygon to the surface, and then regenerate the polygon from those mapped points. By maintaining the proper data structure (a list of lists of points), we're able to keep the polygons separate after they're reduced to a set of points.
The same logic applies here as in the "PolygonsToSurface" node. But instead of mapping polygonal points, we're mapping control points of a nurbs curve.
OffsetPointsToSurface
This node gets a little more complex, but the concept is simple: like the "PointsToSurface" node, this node maps points from one surface to another. However, it also considers points which are not on the original source surface, gets their distance to the closest UV parameter, and maps this distance to the target surface normal at the corresponding UV coordinate. This will make more sense when looking at the example files.
This is a simple node which creates a parametric surface to map from the source grid to an undulating surface in the example files.
The example files can be found in the package's root folder. Click Package Manager > Installed Packages tab.
Next to MapToSurface, click the vertical dots menu > Show Root Directory.
Next, open the "extra" folder, which houses all of the files in the package which are not custom nodes. This is where examples files (if they exist) are stored for Dynamo packages. The screenshots below discuss the concepts demonstrated in each example file.
This example file demonstrates how "PointsToSurface" may be used to panelize a surface based on a grid of rectangles. This should look familiar, as we demonstrated a similar workflow in the previous chapter.
Using a similar workflow, this exercise file shows a setup for mapping circles (or polygons representing circles) from one surface to another. This uses the "PolygonsToSurface" node.
This example file adds some complexity by working with the "NurbsCrvToSurface" node. The target surface is offset a given distance and the nurbs curve is mapped to the original target surface and the offset surface. From there, the two mapped curves are lofted to create a surface, which is then thickened. This resulting solid has an undulation that is representative of the target surface normals.
This example file demonstrates how to map a pleated polysurface from a source surface to a target surface. The source and target surface are a rectangular surface spanning the grid and a revolved surface, respectively.
The source polysurface mapped from the source surface to the target surface.
Since the custom nodes are able to map different types of curves, this last file references an SVG file exported from Illustrator and maps the imported curves to a target surface.
By parsing through the syntax of a .svg file, curves are translated from .xml format to Dynamo polycurves.
The imported curves are mapped to a target surface. This allows us to explicitly (point-and-click) design a panelization in Illustrator, import into Dynamo, and apply to a target surface.
Dynamo for Revit extends building information modeling with the data and logic environment of a graphical algorithm editor. Its flexibility, coupled with a robust Revit database, offers a new perspective for BIM.
This chapter focuses on the Dynamo workflows for BIM. Sections are primarily exercise-based, since jumping right into a project is the best way to get familiar with a graphical algorithm editor for BIM. But first, let's talk about the beginnings of the program.
As both Revit and Dynamo continue to evolve, you may notice that the Revit version you are working with is not compatible with the Dynamo for Revit version you have installed on your machine. Below outlines which versions of Dynamo for Revit are compatible with Revit.
2013
2014
2015
2016
2017
2018
2019
2020+
2.1.0 - Revit 2020+ now includes Dynamo and receives updates as Revit does.)
N/A
With a dedicated team of developers and a passionate community, the project has come a long way from its humble beginnings.
Dynamo was originally created to streamline AEC workflows in Revit. While Revit creates a robust database for every project, it can be difficult for an average user to access this information outside of the constraints of the interface. Revit hosts a comprehensive API (Application Program Interface), allowing third-party developers to create custom tools. And programmers have been using this API for years, but text-based scripting isn't accessible to everyone. Dynamo seeks to democratize Revit data through an approachable graphical algorithm editor.
Using the core Dynamo nodes in tandem with custom Revit ones, a user can substantially expand parametric workflows for interoperability, documentation, analysis, and generation. With Dynamo, tedious workflows can be automated while design explorations can thrive.
In a Revit project or family editor, navigate to Add-ins and click Dynamo.*
*Dynamo will run only in the file in which it was opened.
When opening Dynamo in Revit, there is a new category called "Revit". This is a comprehensive addition to the UI which offers nodes specifically catering to Revit workflows.*
*By using the Revit-specific family of nodes, the Dynamo graph will only work when opening in Dynamo for Revit. If a Dynamo for Revit graph is opened in Dynamo Sandbox for example, the Revit nodes will be missing.
Since Revit is a platform which provides robust project management, parametric operations in Dynamo can be complex and slow to calculate. If Dynamo is taking a long time to calculate nodes, you may want to use the "freeze" node functionality in order to pause the execution of Revit operations while you develop your graph.
You can read more about freezing nodes in the Nodes and Wires section.
Since Dynamo was originally created for AEC, its large and growing community is a great resource for learning from and connecting with experts in the industry. Dynamo’s community is made of architects, engineers, programmers, and designers who all have a passion for sharing and making.
Dynamo is an open-source project that is constantly evolving, and a lot of development is Revit-related. If you're new to the game, get on the discussion forum and start posting questions! If you're a programmer and want to get involved in Dynamo's development, check out the Github repository. Also, a great resource for third-party libraries is the Dynamo package manager. Many of these packages are made with AEC in mind, and we'll take a look at third-party packages for panelization in this chapter.
Dynamo also maintains an active blog. Read up on recent posts to learn about the latest developments!
A powerful feature of Dynamo is that you can edit parameters on a parametric level. For example, a generative algorithm or the results of a simulation can be used to drive the parameters of an array of elements. This way, a set of instances from the same family can have custom properties in your Revit project.
Instance parameters define the aperture of the panels on the roof surface, ranging from an Aperture Ratio of 0.1 to 0.4.
Type-based parameters are applied to every element on the surface because they are the same family type. The material of each panel, for example, can be driven by a type-based parameter.
If you've set up a Revit family before, remember that you have to assign a parameter type (string, number, dimension, etc.) Be sure to use the correct data type when assigning parameters from Dynamo.
You can also use Dynamo in combination with parametric constraints defined in a Revit family's properties.
As a quick review of parameters in Revit, we recall that there are type parameters and instance parameters. Both can be edited from Dynamo, but we'll work with instance parameters in the exercise below.
As of version 0.8, Dynamo is fundamentally unitless. This allows Dynamo to remain an abstract visual programming environment. Dynamo nodes that interact with Revit dimensions will reference the Revit project's units. For example, if you are setting a length parameter in Revit from Dynamo, the number in Dynamo for the value will correspond to the default units in the Revit project. The exercise below works in meters.
For a quick conversion of units, use the "Convert Between Units" node. This is a handy tool for converting Length, Area, and Volume units on the fly.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
The exercise below works in meters.
This exercise focuses on editing Revit elements without performing geometric operation in Dynamo. We're not importing Dynamo geometry here, just editing parameters in a Revit project. This exercise is basic, and to the more advanced Revit users, notice that these are instance parameters of a mass, but the same logic can be applied to an array of elements to customize on a large scale. This is all done with the "Element.SetParameterByName" node.
Begin with the example Revit file for this section. We've removed the structural elements and adaptive trusses from the previous section. In this exercise, we will focus on a parametric rig in Revit and manipulating in Dynamo.
Selecting the building in Mass in Revit, we see an array of instance parameters in the properties panel.
In Dynamo, we can retrieve the parameters by selecting the targeting element.
Select the building mass with the "Select Model Element" node.
We can query all of the parameters of this mass with the "Element.Parameters" node. This includes type and instance parameters.
Reference the Element. Parameters node to find target parameters. Or, we can view the properties panel from the previous step to choose which parameter names we want to edit. In this case, we are looking for the parameters which affect the large geometric moves on the building mass.
We will make changes to the Revit element using the Element.SetParameterByName node
Use C_ode Block to_ define a list of parameters, with quotes around each item to denote a string. We can also use the List.Create node with a series of "string" nodes connected to multiple inputs but Code block is faster and easier. Make sure that the string matches the exact name in Revit, case-specific:
{"BldgWidth","BldgLength","BldgHeight", "AtriumOffset", "InsideOffset","LiftUp"};
We also want to designate values for each parameter. Add six "integer sliders" to the canvas and rename to the corresponding parameter in the list. Also, set the values of each slider to the image above. In order from top-to-bottom: 62,92,25,22,8,12
Define another code block with a list of the same length as the parameter names. In this case, we name variables (without quotes) which create inputs for the code block. Plug the sliders into each respective input:
{bw,bl,bh,ao,io,lu};
Connect the Code Block to the "Element.SetParameterByName"* value input. With run automatically checked, we will automatically see results.
*This demonstration works with instance parameters, but not type parameters.
Just as in Revit, many of these parameters are dependent on each other. There are of course combinations where the geometry may break. We can remedy this issue with defined formulas in the parameter properties, or we can setup a similar logic with math operations in Dynamo (this is an additional challenge if you'd like to expand on the exercise).
This combination gives a funky new design to the building mass: 100, 92, 100, 25, 13, 51
Next, let's look at how we can edit the facade using a similar process.
Copy the graph and focus on the facade glazing which will house the truss system. We isolate four parameters in this case:
{"DblSkin_SouthOffset","DblSkin_MidOffset","DblSkin_NorthOffset","Facade Bend Location"};
Additionally, we create number sliders and rename to the appropriate parameters. The first three sliders from top-to-bottom should be remapped to a domain of [0,10], while the final slider, "Facade Bend Location", should be remapped to a domain of [0,1]. These values, from top-to-bottom should start with these values (although they're arbitrary): 2.68, 2.64, 2.29, 0.5
Define a new Code block and connect the sliders:
{so,mo,no,fbl};
By changing the sliders in this part of the graph, we can make the facade glazing much more substantial: 9.98, 10.0, 9.71 ,0.31
/
/
/
As you discover the wide-reaching application of editing parameters, you may want to edit a large quantity of elements in Revit with Dynamo. This can be a computationally expensive operation, meaning that it can be slow. If you're editing a large number of elements, you may want to use the "freeze" node functionality in order to pause the execution of Revit operations while you develop your graph. For more information on freezing nodes, check out the "" section in the solids chapter.
Editing parameters for documentation follows suit with the lessons learned in prior sections. In this section, we'll look at editing parameters which don't affect the geometric properties of an element, but instead prepare a Revit file for documentation.
In the exercise below, we'll use a basic deviation from plane node to create a Revit sheet for documentation. Each panel on our parametrically defined roof structure has a different value for deviation, and we want to call out the range of values using color and by scheduling out the adaptive points to hand off to a facade consultant, engineer, or contractor.
The deviation from plane node will calculate the distance that the set of four points varies from the best-fit plane between them. This is a quick and easy way to study constructability.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
Start with the Revit file for this section (or continue from the previous section). This file has an array of ETFE panels on the roof. We'll reference these panels for this exercise.
Add a Family Types node to the canvas and choose "ROOF-PANEL-4PT".
Plug this node into a Select All Elements of Family Type node to get all of the elements from Revit into Dynamo.
Query the location of adaptive points for each element with the AdaptiveComponent.Locations node.
Create a polygon from these four points with the Polygon.ByPoints node. Notice we now have an abstract version of the paneled system in Dynamo without having to import the full geometry of the Revit element.
Calculate planar deviation with the Polygon.PlaneDeviation node.
Just for kicks, like the previous exercise, let's set the aperture ratio of each panel based on its planar deviation.
Add an Element.SetParameterByName node to the canvas and connect the adaptive components to the element input. Connect a Code Block reading "Aperture Ratio" into the parameterName input.
We cannot directly connect the deviation results into the value input because we need to remap the values to the parameter range.
Using Math.RemapRange, remap the deviation values to a domain between 0.15 and 0_._45 by entering
0.15; 0.45;
into the Code Block.Plug these results into the value input for Element.SetParameterByName.
Back in Revit we can kind of make sense of the change in aperture across the surface.
Zooming in, it becomes more clear that the closed panels are weighted towards the corners of the surface. The open corners are towards the top. The corners represent areas of larger deviation while bulge has minimal curvature, so this makes sense.
Setting the Aperture Ratio doesn't clearly demonstrate the deviation of panels on the roof, and we're also changing the geometry of the actual element. Suppose we just want to study the deviation from the standpoint of fabrication feasibility. It would be helpful to color the panels based on deviation range for our documentation. We can do that with the series of steps below, and in a very similar process to the steps above.
Remove the Element.SetParameterByName and its input nodes and add Element.OverrideColorInView.
Add a Color Range node to the canvas and plug into the color input of Element.OverrideColorInView. We still have to connect the deviation values to the color range in order to create the gradient.
Hovering over the value input, we can see that the values for the input must be between 0 and 1 in order to map a color to each value. We need to remap the deviation values to this range.
Using Math.RemapRange, remap the planar deviation values to a range between* 0* and 1 (note: you can use the "MapTo" node to define a source domain as well).
Plug the results into a Color Range node.
Notice our output is a range of colors instead of a range of numbers.
If you're set to Manual, hit Run. You should be able to get away with being set to Automatic from this point forward.
Back in Revit, we see a much more legible gradient which is representative of planar deviation based on our color range. But what if we want to customize the colors? Notice that the minimum deviation values are represented in red, which seems to be the opposite of what we'd expect. We want to have maximum deviation to be red, with minimum deviation represented by a calmer color. Let's go back to Dynamo and fix this.
Using a code block, add two numbers on two different lines:
0;
and255;
.Create a red and blue color by plugging the appropriate values into two Color.ByARGB nodes.
Create a list from these two colors.
Plug this list into the colors input of the Color Range, and watch the custom color range update.
Back in Revit, we can now make better sense of areas of maximum deviation in the corners. Remember, this node is for overriding a color in a view, so it can be really helpful if we had a particular sheet in the set of drawings which focuses on a particular type of analysis.
Selecting one ETFE panel in Revit, we see that there are four instance parameters, XYZ1, XYZ2, XYZ3, and XYZ4. These are all blank after they're created. These are text-based parameters and need values. We'll use Dynamo to write the adaptive point locations to each parameter. This helps interoperability if the geometry needs to be sent to an engineer of facade consultant.
In a sample sheet, we have a large, empty schedule. The XYZ parameters are shared parameters in the Revit file, which allows us to add them to the schedule.
Zooming in, the XYZ parameters are yet to be filled in. The first two parameters are taken care of by Revit.
To write in these values, we'll do a complex list operation. The graph itself is simple, but the concepts build heavily from the list mapping as discussed in the list chapter.
Select all the adaptive components with two nodes.
Extract the location of each point with AdaptiveComponent.Locations.
Convert these points to strings. Remember, the parameter is text-based so we need to input the correct data type.
Create a list of the four strings which define the parameters to change: XYZ1, XYZ2, XYZ3, and XYZ4.
Plug this list into the parameterName input of Element.SetParameterByName.
Connect Element.SetParameterByName into the the combinator input of List.Combine. Connect the adaptive components into list1. Connect String from Object into list2.
We are list mapping here, because we are writing four values for each element, which creates a complex data structure. The List.Combine node defines an operation one step down in the data hierarchy. This is why element and value inputs of Element.SetParameterByName are left blank. List.Combine is connecting the sublists of its inputs into the empty inputs of Element.SetParameterByName, based on the order in which they are connected.
Selecting a panel in Revit, we see now that we have string values for each parameter. Realistically, we would create a simpler format to write a point (X,Y,Z). This can be done with string operations in Dynamo, but we're bypassing that here to stay within the scope of this chapter.
A view of the sample schedule with parameters filled in.
Each ETFE panel now has the XYZ coordinates written for each adaptive point, representing the corners of each panel for fabrication.
Dynamo for Civil 3D contains a very powerful mechanism for "remembering" the objects that are created by each node. This mechanism is called Object Binding, and it enables a Dynamo graph to produce consistent results every time it is run in the same document. While this is highly desirable in many situations, there are other situations where you may want to have more control over Dynamo's behavior. This section will help you understand how object binding works and how you can take advantage of it.
Consider this graph that creates a Circle in Model Space on the current layer.
Notice what happens when the radius is changed.
This is object binding in action. Dynamo's default behavior is to modify the Circle's radius, rather than creating a new Circle each time the radius input is changed. This is because the Object.ByGeometry node "remembers" that it created this specific Circle each time the graph is run. Furthermore, Dynamo will store this information away so that the next time you open the Civil 3D document and run the graph, it will have the exact same behavior.
Let's look at an example where you might want to change Dynamo's default object binding behavior. Let's say you want to build a graph that places Text in the middle of a Circle. However, your intent with this graph is that it can be run over and over again and place new Text each time for whatever Circle is selected. Here's what that graph might look like.
However, this is actually what happens when a different Circle is selected.
It seems like the Text is deleted and re-created with each run of the graph. In reality, the position of the Text is being modified depending on which Circle is selected. So it is the same Text, just in a different spot! In order to create new Text every time, we need to modify Dynamo's object binding settings so that no binding data is retained (see Binding Settings below).
After making that change, we get the behavior that we're looking for.
Dynamo for Civil 3D allows for modifying the default object binding behavior through the Binding Data Storage settings in the Dynamo menu.
Note that the Binding Data Storage options are available in Civil 3D 2022.1 and above.
All of the options are enabled by default. Here's a summary of what each option does.
When this option is enabled, Dynamo will "forget" about the objects it created the last time the graph was run. So the graph can be run in any drawing in any situation and it will create new objects every time.
When to Use
Use this option when you want Dynamo to "forget" everything about what it did in previous runs and create new objects every time.
This option means that object binding metadata will be serialized into the graph (.dyn file) when it is saved. If you close/reopen the graph and run it in the same drawing, then everything should work the same as you left it. If you run the graph in a different drawing, the binding data will be removed from the graph and new objects will be created. This means that if you open the original drawing and run the graph again, new objects will be created in addition to the old ones.
When to Use
Use this option when you want Dynamo to “remember” the objects it created the last time it ran in a specific drawing.
This option is best suited for situations where it is possible to maintain a 1:1 relationship between a specific drawing and a Dynamo graph. Options 1 and 3 are better suited for graphs that are designed to run on multiple drawings.
This is similar to Option 2, except that the object binding data is serialized in the drawing instead of the graph (.dyn file). If you close/reopen the graph and run it in the same drawing, then everything should work the same as you left it. If you run the graph in a different drawing, the binding data is still preserved in the original drawing since it is saved in the drawing and not the graph.
When to Use
Use this option when you want to use the same graph across multiple drawings and have Dynamo “remember” what it did in each one.
The first thing to note with this option is that it has no effect on how the graph interacts with the drawing when running the graph through the main Dynamo interface. This option only applies when the graph is run using Dynamo Player.
If Dynamo Player is new to you, take a look at the Dynamo Player section.
If you run the graph using the main Dynamo interface and then close out and run the same graph using Dynamo Player, it will create new objects on top of the ones it created before. However, once Dynamo Player has executed the graph once, it will serialize object binding data in the drawing. So if you run the graph multiple times through Dynamo Player, it will update objects instead of creating new ones. If you run the graph through Dynamo Player on a different drawing, the binding data is still preserved in the original drawing since it is saved in the drawing and not the graph.
When to Use
Use this option when you want to run a graph using Dynamo Player across multiple drawings and have it “remember” what it did in each one.
Dynamo Player provides a simplified way to run Dynamo graphs in Civil 3D. Once a graph has been created, no Dynamo expertise is required to use the Player and run the graphs. This makes it easy to share graphs with others that might not be interested in digging into the details of nodes and wires.
For more information about Dynamo Player in Civil 3D, refer to the documentation provided on the Civil 3D Help site.
Dynamo is a great door to start coding for the AEC world. You may be interested in some of these sections to start your coding journey:
The following Python scripts generate point arrays for several examples. They should be pasted into a Python Script node as follows:
python_points_1
python_points_2
python_points_3
python_points_4
python_points_5
Intersect, Trim, and SelectTrim are primarily used on lower-dimensional geometry such as Points, Curves, and Surfaces. Solid geometry on the other hand, has an additional set of methods for modifying form after their construction, both by subtracting material in a manner similar to Trim and combining elements together to form a larger whole.
The Union method takes two solid objects and creates a single solid object out of the space covered by both objects. The overlapping space between objects is combined into the final form. This example combines a Sphere and a Cuboid into a single solid Sphere-Cube shape:
The Difference method, like Trim, subtracts away the contents of the input tool solid from the base solid. In this example we carve out a small indentation out of a sphere:
The Intersect method returns the overlapping Solid between two solid Inputs. In the following example, Difference has been changed to Intersect, and the resulting Solid is the missing void initially carved out:
Why would you use textual programming in Dynamo's visual programming environment? Visual programming has many advantages. It allows you to create programs without learning special syntax in an intuitive visual interface. However, a visual program can become cluttered, and can at times fall short in functionality. For example, Python offers much more achievable methods for writing conditional statements (if/then) and looping. Python is a powerful tool that can extend the capabilities of Dynamo and allow you to replace many nodes with a few concise lines of code.
Visual Program:
Textual Program:
Like code blocks, Python nodes are a scripting interface within a visual programming environment. The Python node can be found under Script>Editor>Python Script in the library.
Double clicking the node opens the python script editor (you can also right click on the node and select Edit...). You’ll notice some boilerplate text at the top, which is meant to help you reference the libraries you’ll need. Inputs are stored in the IN array. Values are returned to Dynamo by assigning them to the OUT variable
The Autodesk.DesignScript.Geometry library allows you to use dot notation similar to Code Blocks. For more information on Dynamo syntax, refer to https://github.com/DynamoDS/DynamoPrimerNew/blob/master/coding-in-dynamo/7_code-blocks-and-design-script/7-2_design-script-syntax.md as well as the DesignScript Guide (To download this PDF doc, please right-click on link and choose "Save link as..."). Typing a geometry type such as 'Point.' will bring up a list of methods for creating and querying points.
Methods include constructors such as ByCoordinates, actions like Add, and queries like X, Y and Z coordinates.
Download the example file by clicking on the link below.
A full list of example files can be found in the Appendix.
In this example, we will write a python script that creates patterns from a solid module, and turn it into a custom node. First, let’s create our solid module using Dynamo nodes.
Rectangle.ByWidthLength: Create a rectangle that will be the base of our solid.
Surface.ByPatch: Connect the rectangle to the ‘closedCurve’ input to create the bottom surface.
Geometry.Translate: Connect the rectangle to the ‘geometry’ input to move it up, using a code block to specify the base thickness of our solid.
Polygon.Points: Query the translated rectangle to extract the corner points.
Geometry.Translate: Use a code block to create a list of four values corresponding to the four points, translating one corner of the solid up.
Polygon.ByPoints: Use the translated points to reconstruct the top polygon.
Surface.ByPatch: Connect the polygon to create the top surface.
Now that we have our top and bottom surfaces, let’s loft between the two profiles to create the sides of the solid.
List.Create: Connect the bottom rectangle and the top polygon to the index inputs.
Surface.ByLoft: Loft the two profiles to create the sides of the solid.
List.Create: Connect the top, side, and bottom surfaces to the index inputs to create a list of surfaces.
Solid.ByJoinedSurfaces: Join the surfaces to create the solid module.
Now that we have our solid, let’s drop a Python Script node onto the workspace.
To add additional inputs to the node, click the + icon on the node. The inputs are named IN[0], IN[1], etc. to indicate that they represent items in a list.
Let’s start by defining our inputs and output. Double click the node to open the python editor. Follow the code below to modify the code in the editor.
This code will make more sense as we progress in the exercise. Next we need to think about what information is required in order to array our solid module. First, we will need to know the dimensions of the solid to determine the translation distance. Due to a bounding box bug, we will have to use the edge curve geometry to create a bounding box.
Take a look at the Python node in Dynamo. Notice that we're using the same syntax as we see in the titles of the nodes in Dynamo. Check out the commented code below.
Since we will be both translating and rotating the solid modules, let’s use the Geometry.Transform operation. By looking at the Geometry.Transform node, we know that we will need a source coordinate system and a target coordinate system to transform the solid. The source is the context coordinate system of our solid, while the target will be a different coordinate system for each arrayed module. That means we will have to loop through the x and y values to transform the coordinate system differently each time.
Click Run then Save the code. Connect the Python node with our existing script as following.
Connect the output from Solid.ByJoinedSurfaces as the first input for the Python Node and use a Code Block to define the other inputs.
Create a Topology.Edges node and use the output from Python node as its input.
Finally, create an Edge.CurveGeometry node and use the output from Topology.Edges as its input.
Try changing the seed value to create different patterns. You can also change the parameters of the solid module itself for different effects.
Now that we have created a useful python script, let’s save it as a custom node. Select the python script node, right-click on Workspace and select ‘Create Custom Node.’
Assign a name, description and category.
This will open a new workspace in which to edit the custom node.
Inputs: Change the input names to be more descriptive and add data types and default values.
Output: Change the output name
Save the node as a .dyf file and you should see the custom node reflects the changes we just made.
There are two fundamental ways to create free-form curves in Dynamo: specifying a collection of Points and having Dynamo interpolate a smooth curve between them, or a more low-level method by specifying the underlying control points of a curve of a certain degree. Interpolated curves are useful when a designer knows exactly the form a line should take, or if the design has specific constraints for where the curve can and cannot pass through. Curves specified via control points are in essence a series of straight line segments which an algorithm smooths into a final curve form. Specifying a curve via control points can be useful for explorations of curve forms with varying degrees of smoothing, or when a smooth continuity between curve segments is required.
To create an interpolated curve, simply pass in a collection of Points to the NurbsCurve.ByPoints method.
The generated curve intersects each of the input points, beginning and ending at the first and last point in the collection, respectively. An optional periodic parameter can be used to create a periodic curve which is closed. Dynamo will automatically fill in the missing segment, so a duplicate end point (identical to the start point) isn’t needed.
NurbsCurves are generated in much the same way, with input points represent the endpoints of a straight line segment, and a second parameter specifying the amount and type of smoothing the curve undergoes, called the degree.* A curve with degree 1 has no smoothing; it is a polyline.
A curve with degree 2 is smoothed such that the curve intersects and is tangent to the midpoint of the polyline segments:
Dynamo supports NURBS (Non-uniform rational B-spline) curves up to degree 20, and the following script illustrates the effect increasing levels of smoothing has on the shape of a curve:
Note that you must have at least one more control point than the degree of the curve.
Another benefit of constructing curves by control vertices is the ability to maintain tangency between individual curve segments. This is done by extracting the direction between the last two control points, and continuing this direction with the first two control points of the following curve. The following example creates two separate NURBS curves which are nevertheless as smooth as one curve:
*This is a very simplified description of NURBS curve geometry, for a more accurate and detailed discussion see Pottmann, et al, 2007, in the references.