Python and Civil 3D
Last updated
Last updated
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.
🎯 Get the boundary geometry of all Catchments in a drawing.
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.
Dynamo Type | Wraps |
---|---|
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.
🎉 Mission accomplished!
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.