CNC Direct – Export Wall Parts to DXF and SAT

I implemented the Revit ExportCncFab add-in back in 2013 to export Revit wall parts to DXF or SAT for CNC fabrication.

The full source code, Visual Studio solution and add-in manifest lives in the ExportCncFab GitHub repository.

The project was prompted by William Spier, who recently published a nice 12-minute YouTube video Revit to CNC Direct describing and demonstrating its practical use and application:

In William's words: "Asked Jeremy to write a DLL that allows users to export multiple objects as discrete, individually named DXF files. So doing means you can export native Revit geometry to DXF, resulting in fabrication ready files like gyp wall board, structural steel gussets, or anything you can conceive of that has a level of accuracy that allows it to be CNC ready, as it exists in the Revit model."

Due to popular request, I now migrated this add-in from Revit 2014 to Revit 2015 and on to 2016, documenting all issues encountered en route.

Before getting to that, here are the previous discussions of this project and a couple of its implementation aspects:

Now, let's look at the two migrations in more detail:

Migrating ExportCncFab from Revit 2014 to Revit 2015

I performed the standard steps to migrate a Revit add-in from Revit 2014 to Revit 2015:

The good news is that the flat migrated add-in compiles successfully.

The warnings are not really bad news, either; they just indicate usage of some deprecated API functionality, which is completely normal.

I fixed them right away in order to simplify to continued migration to the next major release.

The six warnings are actually just three, with some repetitions:

Let's take a closer look at these three warnings and how to fix them:

Replacing Selection.Elements by GetElementIds

Instead of accessing a collection of the currently selected elements directly, Revit now provides a list of their element ids.

The code updated to compile with no warnings in Revit 2015 looks like this, with the obsolete code commented out:

  // Iterate over all pre-selected parts
 
  List<ElementId> ids = null;
 
  Selection sel = uidoc.Selection;
 
  ICollection<ElementId> selIds = sel.GetElementIds(); // 2015
 
  //if( 0 < sel.Elements.Size ) // 2014
 
  if( 0 < selIds.Count ) // 2015
  {
    //foreach( Element e in sel.Elements ) // 2014
 
    foreach( ElementId id in selIds ) // 2015
    {
      Element e = doc.GetElement( id );
 
      if( !( e is Part ) )
      {
        ErrorMsg( "Please pre-select only gyp wallboard"
          + " parts before running this command." );
        return Result.Failed;
      }
 
      Part part = e as Part;
 
      // . . .

Replacing Element.get_Parameter by Element.GetParameters

Multiple parameters of the same name can be attached to a single Revit element.

In order to force developers to handle this possibility in a fool-proof way, the get_Parameter method taking a parameter name is deprecated in Revit 2015 and can be replaced by the GetParameters method that returns a collection of all the parameters of the given name instead.

Here is an example of some Revit 2014 code that causes the corresponding warning:

  /// <summary>
  /// Return the parameter definition from
  /// the given element and parameter name.
  /// </summary>
  static Definition GetDefinition(
    Element e,
    string parameter_name )
  {
    Parameter p = e.get_Parameter( parameter_name );
 
    Definition d = ( null == p )
      ? null
      : p.Definition;
 
    return d;
  }

Here is possibly way of fixing this to compile cleanly in Revit 2015:

  /// <summary>
  /// Return the parameter definition from
  /// the given element and parameter name.
  /// </summary>
  static Definition GetDefinition(
    Element e,
    string parameter_name )
  {
    IList<Parameter> ps = e.GetParameters( parameter_name );
 
    int n = ps.Count;
 
    Debug.Assert( 1 >= n,
      "expected maximum one shared parameters "
      + "named " + parameter_name );
 
    Definition d = ( 0 == n )
      ? null
      : ps[0].Definition;
 
    return d;
  }

Using ExternalDefinitonCreationOptions

This is the code used to define the ExportCncFab shared parameters in Revit 2014 using the DefinitionGroup.Definitions.Create method:

  // Create the category set for binding
 
  CategorySet catSet = app.Create.NewCategorySet();
 
  Category cat = doc.Settings.Categories.get_Item(
    BuiltInCategory.OST_Parts );
 
  catSet.Insert( cat );
 
  Binding binding = app.Create.NewInstanceBinding(
    catSet );
 
  // Retrieve or create shared parameter group
 
  DefinitionGroup group
    = f.Groups.get_Item( _definition_group_name )
    ?? f.Groups.Create( _definition_group_name );
 
  // Retrieve or create the three parameters;
  // we could check if they are already bound, 
  // but it looks like Insert will just ignore 
  // them in that case.
 
  Definition definition
    = group.Definitions.get_Item( _is_exported )
    ?? group.Definitions.Create( _is_exported,
      ParameterType.YesNo, true );
 
  doc.ParameterBindings.Insert( definition, binding,
    BuiltInParameterGroup.PG_GENERAL );
 
  definition
    = group.Definitions.get_Item( _exported_first )
    ?? group.Definitions.Create( _exported_first,
      ParameterType.Text, true );
 
  doc.ParameterBindings.Insert( definition, binding,
    BuiltInParameterGroup.PG_GENERAL );
 
  definition
    = group.Definitions.get_Item( _exported_last )
    ?? group.Definitions.Create( _exported_last,
      ParameterType.Text, true );
 
  doc.ParameterBindings.Insert( definition, binding,
    BuiltInParameterGroup.PG_GENERAL );

In Revit 2015, this causes deprecated API usage warnings prompting to use the ExternalDefinitonCreationOptions class instead.

To avoid fixing this three times over and using extremely long wrapped lines to do so, I defined the local static CreateNewDefinition helper method to handle this for 2015 and replace the calls to group.Definitions.Create above:

  static Definition CreateNewDefinition(
    DefinitionGroup group,
    string parameter_name,
    ParameterType parameter_type )
  {
    //return group.Definitions.Create( 
    //  parameter_name, parameter_type, true ); // 2014
 
    return group.Definitions.Create(
      new ExternalDefinitonCreationOptions(
        parameter_name, parameter_type ) ); // 2015
  }

It now compiles with zero errors and zero warnings.

This state of things is published as release 2015.0.0.2.

Migrating ExportCncFab from Revit 2015 to Revit 2016

The flat migration from Revit 2015 to Revit 2016 generates one trivial but nonetheless irritating error CS0246 affecting the CreateNewDefinition method that we just introduced above:

This is simply due to a typo in the original Revit 2015 API class name that has been rectified in Revit 2016.

The fix is equally trivial and obvious: add the missing letter 'i'.

The new implementation including commented obsolete code now looks like this:

  static Definition CreateNewDefinition(
    DefinitionGroup group,
    string parameter_name,
    ParameterType parameter_type )
  {
    //return group.Definitions.Create( 
    //  parameter_name, parameter_type, true ); // 2014
 
    //return group.Definitions.Create(
    //  new ExternalDefinitonCreationOptions(
    //    parameter_name, parameter_type ) ); // 2015
 
    return group.Definitions.Create(
      new ExternalDefinitionCreationOptions(
        parameter_name, parameter_type ) ); // 2016
  }

Once that was fixed, no more errors or warnings were generated.

This initial Revit 2016 version is published as release 2016.0.0.0.

I hope you find this useful and look forward to hearing how you are making use of it!