Flatten All Elements to DirectShape

I am still busy preparing my presentations for Autodesk University and making slow progress due to handling too many Revit API issues in between.

And blogging, as well.

Here is a new cool sample contributed by Nikolay Shulga, Senior Principal Engineer in the Revit development team.

In his own words:

This fits in well with the growing interest in direct shapes, as you can observe by the rapidly growing number of entries in the DirectShape topic group.

I implemented a new external command CmdFlatten in The Building Coder samples to test and demonstrate this functionality.

Nikolay's original code was for a future release of Revit, so some backwards adaptation was necessary.

You can see the changes by looking at the last few GitHub commits.

Here is the final result for running in Revit 2016:

  const string _direct_shape_appGUID = "Flatten";
 
  Result Flatten(
    Document doc,
    ElementId viewId )
  {
    FilteredElementCollector col
      = new FilteredElementCollector( doc, viewId )
        .WhereElementIsNotElementType();
 
    Options geometryOptions = new Options();
 
    using( Transaction tx = new Transaction( doc ) )
    {
      if( tx.Start( "Convert elements to DirectShapes" )
        == TransactionStatus.Started )
      {
        foreach( Element e in col )
        {
          GeometryElement gelt = e.get_Geometry(
            geometryOptions );
 
          if( null != gelt )
          {
            string appDataGUID = e.Id.ToString();
 
            // Currently create direct shape 
            // replacement element in the original 
            // document – no API to properly transfer 
            // graphic styles to a new document.
            // A possible enhancement: make a copy 
            // of the current project and operate 
            // on the copy.
 
            DirectShape ds
              = DirectShape.CreateElement( doc,
                e.Category.Id, _direct_shape_appGUID,
                appDataGUID );
 
            try
            {
              ds.SetShape(
                new List<GeometryObject>( gelt ) );
 
              // Delete original element
 
              doc.Delete( e.Id );
            }
            catch( Autodesk.Revit.Exceptions
              .ArgumentException ex )
            {
              Debug.Print(
                "Failed to replace {0}; exception {1} {2}",
                Util.ElementDescription( e ),
                ex.GetType().FullName,
                ex.Message );
            }
          }
        }
        tx.Commit();
      }
    }
    return Result.Succeeded;
  }
 
  public Result Execute(
    ExternalCommandData commandData,
    ref string message,
    ElementSet elements )
  {
    UIApplication uiapp = commandData.Application;
    UIDocument uidoc = uiapp.ActiveUIDocument;
    Document doc = uidoc.Document;
 
    // At the moment we convert to DirectShapes 
    // "in place" - that lets us preserve GStyles 
    // referenced by element shape without doing 
    // anything special.
 
    return Flatten( doc, uidoc.ActiveView.Id );
  }

Here is a trivial example of flattening a wall to a direct shape:

Original wall element

This is the automatically generated DirectShape replacement element, retaining the wall category and storing its original element id:

DirectShape replacement element

Let's try it out on a slightly more complex model:

Walls with doors and windows

The family instances get lost by the conversion in its current implementation:

DirectShape replacement element

If you wish to retain family instances, you should probably explore their geometry in a little bit more detail, e.g., to extract all the solids they contain and convert them individually.

The current version is provided in the module CmdFlatten.cs in The Building Coder samples release 2016.0.123.0.

Have fun playing with this, and many thanks to Nikolay for implementing and sharing it!