Professional Documents
Culture Documents
Getting Started With Dynamo Development
Getting Started With Dynamo Development
Getting Started With Dynamo Development
Find a Page…
Home
This page is outdated! It will be refreshed once some new
Add Icons for a Zero Touch
changes to the node API are in place. Assembly
API Changes
Dynamo is written in a combination of C# and F#, two .NET languages. While you can use any
Visual Studio version ﴾2008 and onwards﴿ to work with the Dynamo Source, Visual Studio will need Releases
to be configured to be able to load and build C# and F# projects.
Release Notes
###DragCanvas The DragCanvas project contains a WPF component based on the Canvas class,
which allows UIElements to be dragged around a canvas using a mouse. Contributing
###DynamoElements DynamoElements is the main Dynamo project, and is ﴾probably﴿ the one you Choosing A Pull Request
will be spending most of your time in. DynamoElements contains the entire Dynamo UI, all Node Template
Major
defintions, and the program logic. ####Notable Files:
Feature
###DynamoRevit DynamoRevit contains the plugin code for Revit and Vasari. It is where the
How To
Ribbon button is setup and where initialization takes place for the UI and the Dynamic Model
Updater. Create your own library for
Dynamo
###FScheme The FScheme project serves as the foundation for running Dynamo scripts. It's what Zero Touch
handles the order of execution and passing arguments to nodes as inputs. In reality, FScheme is a Write a Library with
programming language based on an existing language called Scheme. FScheme is written in F# due ZeroTouch
to a close relationship with the language ﴾both FSharp and FScheme are functional programming Write unit tests for your
languages﴿, and because F# has Tail Call Optimizations built into the compiler, which allows for the Dynamo library
construction of recursive calls of unlimited depth without worrying about stack overflows. Add Icons for a Zero Touch
Assembly
###FSchemeInterop FSchemeInterop contains wrappers which are used to quickly convert C# code Migration of
ProtoInterface to
to code blocks executable from FScheme. Odds are, you will be using some utility functions
DynamoServices
contained in this project when writing new nodes.
Integrate Dynamo with another
##Writing your first new Node host
###Getting Setup First thing's first: make sure that Dynamo is building properly before you start to Integrate localized resource
files
modify it. You can start a build by clicking Build > Build Solution ﴾or by pressing F6 by default﴿. If it
says "Build Successful" at the bottom of the screen, then you're good to go! If not, then you will Write usable error messages
see some errors which need to be fixed.
Build Dynamo on Linux, Mac
Assuming you're now building correctly, we're ready to write a node! Make a new file called Efficiently Working With Large
MyFirstNode.cs in the DynamoElements project ﴾since that's where Nodes go﴿. Data Sets In Dynamo
First, you'll want to tweak the auto‐generated code. Your class MyFirstNode has to extend dynNode Run Dynamo from the
command line
in order to be a Node. You will also want to add a few using statements:
Build A Package for Dynamo
with Visual Studio
using System;
using System.Collections.Generic;
using System.Linq; Dynamo Internals
using System.Text;
How Replication and
using Microsoft.FSharp.Collections; Replication Guide work: Part 1
using Dynamo.Connectors; How Replication and
using Value = Dynamo.FScheme.Value; Replication Guide work: Part 2
How Replication and
namespace Dynamo.Elements
Replication Guide work: Part 3
{
class MyFirstNode : dynNode
{ Libraries
}
} Dynamo Revit.
Dynamo Mesh Toolkit.
The two extra using statements are necessary for proper code‐style in the Dynamo project. They
Installers
are also fairly convenient and will save you a lot of unnecessary typing.
Dynamo Installers
###Defining the Node
In this example, we will be creating a node that takes the maximum of two given numbers. So this FAQs
node will take in two numbers as inputs, and return one number as a result. In Dynamo, it's not
Dynamo FAQ
necessary for any node to take in input, but ALL nodes must have one and only one output.
Python FAQ
While these aren't the only attributes you can use for a node, these are the ones you should use for Clone in Desktop
every node you define.
ElementName﴾string﴿ ‐ The name of your node, this must be unique. It will also be what the
user sees in Dynamo.
ElementCategory﴾string﴿ ‐ The category this node belongs to in the side‐menu. You can put
whatever string you like here, and if the category doesn't already exist, it will be created for the
node. If you want to reference any of the built‐in categories, however, it is recommended you
reference it via the BuiltInElementCategories class. This is important if, in the future, the names
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 2/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
of the categories change. If you reference these instead of hard‐coding the string, you won't
have to update them in the future if they change.
ElementDescription﴾string﴿ ‐ Description of the node, displayed as a ToolTip to the user when
they mouse over the node.
RequiresTransaction﴾bool﴿ ‐ True or false value determining whether or not the Node's logic
should be wrapped in a Revit Transaction. If your node calls any API that requires a
Transaction, then set this to true. Otherwise set it to false for performance purposes.
public MyFirstNode()
{
InPortData.Add(new PortData("num1", "A number to compare", typeof(double)));
InPortData.Add(new PortData("num2", "A number to compare", typeof(double)));
OutPortData = new PortData("max", "The greater of the two inputs", typeof(double));
NodeUI.RegisterAllPorts();
}
Let's take an in‐depth look at this. InPortData is a field inherited from dynElement that is a list of
PortData object, used to define the inputs of the Node. OutPortData is a field inherited from
dynElement that is a single PortData object, used to define the output of the node. PortData is a
class used to define the characteristics of any Port ﴾either Input or Output﴿.
string nickName ‐ The name of the port. This will be displayed as text visually on the node.
string top ‐ Description of the port. This will appear as a ToolTip when the user overs over the
port.
Type portType ‐ Type of object the port produces/accepts. Right now this functionality is not
implemented into Dynamo, but eventually this will be used to enforce Type‐checking
automatically, so that the user cannot connect two nodes without matching types.
Finally, NodeUI.RegisterAllPorts(); tells the UI to update it's layout so that it can display all of the
Ports that have just been defined. This should always be called in the constructor after all of the
ports have been defined.
The Node's logic is the code that will be called when the node is ready to be evaluated. You will
have access to the inputs given to the node, and can use them to calculate the desired output.
The logic is contained in an overridden method inherited from dynNode called Evaluate. Add the
following to your node definition underneath the constructor:
From the method signature, you can see that the logic takes in a List of Expression objects, and
returns an Expression object.
####About Values
In Dynamo, all arguments and return values are objects of type Value. A Value is a container that is
used to differentiate between datatypes under the hood in Dynamo's FScheme internals. Values
come in a few flavors, here are the ones you need to know about:
Value.Number
Value.String
Value.List
Value.Container ‐ Contains any other object.
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 3/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
To access the contents of an Value, you must first cast the Value to the appropriate Value type, and
then get it's contents by using the Item field.
double a = ((Value.Number)numberValue).Item;
string s = ((Value.String)stringValue).Item;
Objects work a little differently: Item for Value.Container will return an object, so that must be cast
to the specific type you want.
Lists require a bit more explanation. Item for Value.List will return a FSharpList. Each element in the
list is another expression, which then must be converted using the same rules as above.
In the Evaluate method, any exceptions that are thrown are automatically caught by Dynamo and
notified to the user. So it's usually common practice to simply cast to what you need and assume it
works. Any failures will be announced to the user, and that means they haven't followed the input
requirements for your node and will have to adjust their inputs.
However, there are situations where you may be expecting inputs of one type or another. In this
case, you can use the Value.Is[Type] fields. For example:
if (value.IsNumber)
{
//Treat value as a number
}
else if (value.IsString)
{
//Treat value as a string
}
//...
We first have to get our desired inputs from the argument list, like so:
double a = ((Value.Number)args[0]).Item;
double b = ((Value.Number)args[1]).Item;
Since we are expecting two inputs, bother numbers, we simply fetch the first two elements from
the input list ﴾args﴿, cast them to Value.Number, and then pull the number using .Item.
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 4/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
return Value.NewNumber(result);
Note that we don't use the new keyword to construct a new Expression. All Expressions are
constructed using the Value.New[Type] factory methods.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.FSharp.Collections;
using Dynamo.Connectors;
using Value = Dynamo.FScheme.Value;
namespace Dynamo.Elements
{
[ElementName("Max")]
[ElementCategory(BuiltinElementCategories.MATH)]
[ElementDescription("Returns the maximum of two given numbers.")]
[RequiresTransaction(false)]
class MyFirstNode : dynElement
{
public MyFirstNode()
{
InPortData.Add(new PortData("num1", "A number to compare", typeof(double)));
InPortData.Add(new PortData("num2", "A number to compare", typeof(double)));
OutPortData = new PortData("max", "The greater of the two inputs", typeof(double));
NodeUI.RegisterAllPorts();
}
}
return Value.NewNumber(result);
}
}
So now we build DynamoElements, copy the new DynamoElements.dll file over to your Dynamo
installation directory, and run Dynamo. You should see your new Max node under the Math section
of the side menu. Test it out!
To demonstrate the proper way to write Revit nodes in Dynamo, we will look at a simple example.
This node will take in an XYZ and return a ReferencePoint located at that XYZ.
NodeUI.RegisterAllPorts();
}
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 5/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
public override Value Evaluate(FSharpList<Value> args)
{
XYZ loc = (XYZ)((Value.Container)args[0]).Item;
return Value.NewContainer(
this.UIDocument.Document.FamilyCreate.NewReferencePoint(loc)
);
}
}
Now, if you were to run this once, it would work fine; when executed, this node will blindly make a
new ReferencePoint at the location of the given XYZ.
However, you will notice that on subsequent runs, it creates new ReferencePoints and keeps the
old ones where they were. With Run Automatically checked in Dynamo, this is problematic.
What we need to do is store a reference to the created ReferencePoint in our code, and then
update that ReferencePoint's position on subsequent Evaluate calls.
All nodes inherit a special list from dynElement called Elements. When an ElementId is placed in the
Elements list, Dynamo will automatically watch those ElementIds to make sure they remain valid on
later runs. This saves the user a lot of effort, since they won't have to keep track of changes in the
document and whether or not the Dynamo transaction was successful or rolled back.
ReferencePoint pt;
//Fin
return Value.NewContainer(pt);
}
So now, we check to see if we've made anything by checking if we've put anything in Elements. If
Elements is empty, we make the Element like normal, and then add it to Elements.
Now let's fill in the code for when we've already stored one.
...
//If we've made any elements previously...
if (this.Elements.Any())
{
Element e;
//...try to get the first one...
if (dynUtils.TryGetElement(this.Elements[0], out e))
{
//..and if we do, update it's position.
pt = e as ReferencePoint;
pt.Position = loc;
}
else
{
//...otherwise, just make a new one and replace it in the list.
pt = this.UIDocument.Document.FamilyCreate.NewReferencePoint(loc);
this.Elements[0] = pt.Id;
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 6/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
}
}
...
Elements stores ElementIds, so we need to fetch the Element from the document. dynUtils contains
a utility method for doing this, called TryGetElement. We pass in the ElementId to fetch ﴾taken
directly from Elements﴿, and we pass in the variable we want to assign the fetched element to.
TryGetElement will return true if it was successful, and false if the ElementId is no longer valid.
So if it's successful, we just cast the element to a ReferencePoint, and then set the position to the
new xyz. If it's unsuccessful, we create a new element like normal, and then update the stored Id in
Elements.
NodeUI.RegisterAllPorts();
}
ReferencePoint pt;
//Fin
return Value.NewContainer(pt);
}
}
Looking for help with using the Dynamo application? Try dynamobim.org.
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 7/8
4/23/2017 Getting Started with Dynamo Development · DynamoDS/Dynamo Wiki
© 2017 GitHub, Inc. Terms Privacy Security Status Help Contact GitHub API Training Shop Blog About
https://github.com/DynamoDS/Dynamo/wiki/GettingStartedwithDynamoDevelopment 8/8