During my recent vacation, I published a description by my colleague Akira Kudo describing how to research the Revit SDK samples to find all the required bits and pieces to solve the task of saving a temporary in-memory Revit solid to an external SAT file.
Victor Chekalin, or Виктор Чекалин, reacted to that and provides a complete Visual Studio project to demonstrate the full implementation of that functionality.
You can download it as a zip archive or clone the project from GitHub.
I grabbed Victor's sample, added a couple of trivial enhancements and included it as a new command CmdExportSolidToSat to The Building Coder sample collection.
It demonstrates the following steps:
One of the steps implements functionality to find the full file path of a given family template file using a recursive directory search method:
/// <summary> /// Return the full path of the first file /// found matching the given filename pattern /// in a recursive search through all /// subdirectories of the given starting folder. /// </summary> string DirSearch( string start_dir, string filename_pattern ) { foreach( string d in Directory.GetDirectories( start_dir ) ) { foreach( string f in Directory.GetFiles( d, filename_pattern ) ) { return f; } string f2 = DirSearch( d, filename_pattern ); if( null != f2 ) { return f2; } } return null; }
With this method in place, I can determine the full path of the standard metric mass family template using a single statement like this:
// Search for the metric mass family template file string template_path = DirSearch( app.FamilyTemplatePath, "Metric Mass.rft" );
The other steps listed above are all pretty standard.
Here is the complete code of the external command:
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; Selection sel = uidoc.Selection; // Retrieve all floors from the model var floors = new FilteredElementCollector( doc ) .OfClass( typeof( Floor ) ) .ToElements() .Cast<Floor>() .ToList(); if( 2 != floors.Count ) { message = "Please create two intersected floors"; return Result.Failed; } // Retrieve the floor solids Options opt = new Options(); var geometry1 = floors[0].get_Geometry( opt ); var geometry2 = floors[1].get_Geometry( opt ); var solid1 = geometry1.FirstOrDefault() as Solid; var solid2 = geometry2.FirstOrDefault() as Solid; // Calculate the intersection solid var intersectedSolid = BooleanOperationsUtils .ExecuteBooleanOperation( solid1, solid2, BooleanOperationsType.Intersect ); // Search for the metric mass family template file string template_path = DirSearch( app.FamilyTemplatePath, "Metric Mass.rft" ); // Create a new temporary family var family_doc = app.NewFamilyDocument( template_path ); // Create a free form element // from the intersection solid using( var t = new Transaction( family_doc ) ) { t.Start( "Add Free Form Element" ); var freeFormElement = FreeFormElement.Create( family_doc, intersectedSolid ); t.Commit(); } string dir = Path.GetTempPath(); string filepath = Path.Combine( dir, "floor_intersection_family.rfa" ); SaveAsOptions sao = new SaveAsOptions() { OverwriteExistingFile = true }; family_doc.SaveAs( filepath, sao ); // Create 3D View var viewFamilyType = new FilteredElementCollector( family_doc ) .OfClass( typeof( ViewFamilyType ) ) .OfType<ViewFamilyType>() .FirstOrDefault( x => x.ViewFamily == ViewFamily.ThreeDimensional ); View3D threeDView; using( var t = new Transaction( family_doc ) ) { t.Start( "Create 3D View" ); threeDView = View3D.CreateIsometric( family_doc, viewFamilyType.Id ); t.Commit(); } // Export to SAT var viewSet = new List<ElementId>() { threeDView.Id }; SATExportOptions exportOptions = new SATExportOptions(); var res = family_doc.Export( dir, "SolidFile.sat", viewSet, exportOptions ); return Result.Succeeded; }
Here is the SAT file SolidFile.sat generated by the two intersecting floors example that I used to test the RvtClipper Boolean operations for 2D polygons:
It generates the following warning when reimported into Revit, but the missing pieces do not make any visibly detectable difference:
They do however affect what Revit considers the size of imported object, because if I zoom the model containing nothing but this SAT import to its extents, the actual geometry appears pretty miniscular:
That need not worry us here, though.
Here is version 2014.0.104.0 of The Building Coder samples source code, Visual Studio solution and RvtSamples include file including the new CmdExportSolidToSat command.
Very many thanks to Victor for providing this nice implementation!