-
Notifications
You must be signed in to change notification settings - Fork 635
Zero Touch Plugin Development
This page outlines the process of developing a custom Dynamo node in C# using the "Zero Touch" interface
An example Visual Studio 2012 project can be found here: https://github.com/hlp/ZeroTouchEssentials
Note: Make sure your Visual Studio project's "Platform target" is set to "Any CPU" or x64, and "Prefer 32-bit" is un-checked.
In most cases, C# static methods and Classes can be imported without modification. If your library only needs to call functions, and not construct new objects, this can be achieved very easily with static methods. When Dynamo loads your DLL, it will strip off the namespace of your classes, and expose all static methods as nodes. For instance, we can create a node that takes a single number and multiplies it by 2 as follows:
namespace ZeroTouchExample
{
public class ZeroTouchExample
{
public static double MultByTwo(double inputNumber)
{
return inputNumber * 2.0;
}
}
}
A node will appear in the Dynamo menu called "MultByTwo" in the "ZeroTouchExample" category. It will have one input, called "inputNumber".
Accepting multiple inputs into a node is easy: just specify multiple parameters to your C# function. Returning multiple values from a node requires slightly more work. First step is to reference "ProtoInterface.dll" in your project, found in the Dynamo install location, and add "using Autodesk.DesignScript.Runtime;" to the top of your C# script. The second step is to add the "MultiReturn" attribute to your function. It should contain an array of strings, containing the names of the output ports. Your function should then return a Dictionary<string, object> containing the returned values. The keys to your dictionary should match the parameter names in your attribute. For example:
using Autodesk.DesignScript.Runtime;
namespace ZeroTouchExample
{
public class ZeroTouchExample
{
[MultiReturn(new[] { "add", "mult" })]
public static Dictionary<string, object> ReturnMultiExample(double a, double b)
{
return new Dictionary<string, object>
{
{ "add", (a + b) },
{ "mult", (a * b) }
};
}
}
}
This creates a new node that outputs two doubles, "add" and "mult" containing the addition and multiplication of the two input numbers.
Dynamo doesn't have a "new" keyword, so objects need to be constructed via static constructors. Dynamo uses the "By" prefix to indicate a static method is a constructor, and while this it is optional, using "By" will help your library better fit into the existing Dynamo style.
namespace ZeroTouchExample
{
public class MyLine
{
private double _x1;
private double _y1;
private double _x2;
private double _y2;
public static MyLine ByStartPointEndPoint(double x1, double y1, double x2, double y2)
{
_x1 = x1;
_y1 = y1;
_x2 = x2;
_y2 = y2;
}
public double Length()
{
return Math.sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
}
}
}
Dynamo libraries can also use native Dynamo geometry types as inputs, and create new geometry as outputs. The first step is to reference both "ProtoGeometry.dll" and "ProtoInterface.dll" in your project, and include "using Autodesk.DesignScript.Interfaces;" and "using Autodesk.DesignScript.Geometry;" in the top of your C# file. Dynamo geometry objects are then used like any other passed object to your functions. For example, we can clean up our MyLine example above by using Dynamo's native geometry types:
using Autodesk.DesignScript.Interfaces;
using Autodesk.DesignScript.Geometry;
namespace ZeroTouchExample
{
public class MyLine
{
private Autodesk.DesignScript.Geometry.Point _p1;
private Autodesk.DesignScript.Geometry.Point _p2;
public static MyLine ByStartPointEndPoint(Autodesk.DesignScript.Geometry.Point p1, Autodesk.DesignScript.Geometry.Point p2)
{
_p1 = p1;
_p2 = p2;
}
public double Length()
{
return Math.sqrt(Math.Pow(_p2.X- _p1.X, 2) + Math.Pow(_p2.Y - _p1.Y, 2));
}
}
}
It's best practice to add documentation to your Dynamo nodes. This is done through XML documentation tags. The main documentation for your node should be in the <summary>...</summary>
XML tags. This will appear as a tooltip over your node in the left search side bar. Documentation on specific input parameters should appear in the <param name="inputName">...</param>
XML tag.
Control over how your node appears in the Dynamo search results is similarly controlled through the <search>...</search>
XML documentation tag. This tag should contain a comma-separated list of search terms that, if matched, will cause your node to appear in search results.
To get Dynamo to pickup the tags you must enable XML documentation generation through visual studio, this setting is in the Build tab of the project settings.
We can add documentation an search to our original example as follows:
namespace ZeroTouchExample
{
public class ZeroTouchExample
{
/// <summary>
/// This is an example node demonstrating how to use the Zero Touch import mechanism.
/// It returns the input number multiplied by 2.
/// </summary>
/// <param name="inputNumber">Number that will get multiplied by 2</param>
/// <search>
/// example, multiply, math
/// </search>
public static double MultByTwo(double inputNumber)
{
return inputNumber * 2.0;
}
}
}
As you publish newer versions of your library, you may want to change the names of ZeroTouch nodes. It is possible to specify these name changes in a migrations file so that graphs built on previous versions of your library don't break when you create an update.
Migration files should be named in the following format: BaseDLLName.Migrations.xml. For instance ProtoGeometry.dll, Dynamo's core geometry library, has a migration file named ProtoGeometry.Migrations.xml. The file needs to be located in the same directory as the base dll.
The migrations file should be valid XML. The file should contain one "migrations" element, containing several "priorNameHint" elements. Each prior name element should have one "oldName" element, and one "newName" element. Old name should contain the complete name, including namespace, of the previous method name. newName should contain the new name, also including complete namespace. See ProtoGeometry.Migrations.xml for a template migrations file.
Note that in its current state, the migrations file gives hints to Dynamo about up how to handle missing nodes in a DLL, but doesn't specify in absolute terms a mapping between versions. So, for instance, if you "depreciate" a node name, then begin re-using the name in a subsequent version, the migration will likely fail.
If you are using the Dynamo geometry library in your C# plugins, you'll need to manually manage the geometry resources created in your functions which are not returned out of your functions. Any resources returned out of your functions will be managed by the Dynamo engine. The are two ways to do this, a "using" statement, or manual Dispose() calls. The using statement is documented here. Here are two examples:
With using
using (Point p1 = new Point.ByCoordinates(0, 0, 0))
{
using (Point p2 = new Point.ByCoordinates(10, 10, 0))
{
return Line.ByStartPointEndPoint(p1, p2);
}
}
With Dispose
Point p1 = new Point.ByCoordinates(0, 0, 0);
Point p2 = new Point.ByCoordinates(10, 10, 0);
Line l = Line.ByStartPointEndPoint(p1, p2);
p1.Dispose();
p2.Dispose();
return l;
Looking for help with using the Dynamo application? Try dynamobim.org.
- Dynamo 2.0 Language Changes Explained
- How Replication and Replication Guide work: Part 1
- How Replication and Replication Guide work: Part 2
- How Replication and Replication Guide work: Part 3