Code-Driven File IO

Prior to Rhino 8, code-driven exporting was done by using the active document and scripting the export command. This was both inefficient and painful to write.

Rhino 8 introduces the abililty to read and write files of any format that Rhino supports entirely through code. This includes support for options that need to be applied to properly read or write different files.

How does it work?

Import and export plug-ins are provided an optional dictionary at read and write time. When the dictionary is present, instead of asking for user input, the importers and exporters pay attention to keys and values in the dictionary to make decisions on options for I/O. RhinoCommon provides classes for these options that can be converted into a dictionary that the import and export plug-ins can interpret. Look in the Rhino.FileIO namespace for format specific option classes.

Example: Write an AutoCAD dwg from Rhino

This example shows how to write the active RhinoDoc to an AutoCAD dwg with options:

var doc = Rhino.RhinoDoc.ActiveDoc;
if (doc != null)
    // create/set options for exporting to DWG file
    var options = new Rhino.FileIO.FileDwgWriteOptions();
    options.UseLWPolylines = true;
    options.Version = Rhino.FileIO.FileDwgWriteOptions.AutocadVersion.Acad2000;
    // convert options into a dictionary
    var optionsDictionary = options.ToDictionary();

    var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop);
    path = System.IO.Path.Combine(path, "sample.dwg");

    // export to a file with our options dictionary
    bool success = doc.Export(path, optionsDictionary);
    if (success)
        System.Console.WriteLine("Successfully exported sample.dwg");
        System.Console.WriteLine("Error while trying to export sample.dwg");
import scriptcontext
import Rhino
import os

if scriptcontext.doc is not None:
    options = Rhino.FileIO.FileDwgWriteOptions()
    options.UseLWPolylines = True
    options.Version = Rhino.FileIO.FileDwgWriteOptions.AutocadVersion.Acad2000
    options_dictionary = options.ToDictionary()

    path = os.path.expanduser("~/Desktop/sample_py.dwg")
    success = scriptcontext.doc.Export(path, options_dictionary)
    if success:
        print("Successfully exported sample.dwg")
        print("Error while trying to export sample.dwg")

Headless Document Support

Headless RhinoDoc instances can be used to further streamline the process. Headless documents are RhinoDoc instances that never affect the Rhino user interface. Their use can improve performance as none of the geometry in the document needs to be displayed in Rhino. You can either create a headless document from scratch and add geometry to it or import from a file using options.

Example: Read a SketchUp file into Rhino

This example shows how to read a SketchUp skp file and example the geometry using a headless RhinoDoc:

// Create a headless doc and import SketchUp file into it
var doc = Rhino.RhinoDoc.CreateHeadless(null);
var options = new Rhino.FileIO.FileSkpReadOptions();
options.ImportCurves = false;
options.EmbedTexturesInModel = false;

var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop);
path = System.IO.Path.Combine(path, "test.skp");
doc.Import(path, options.ToDictionary());

// Analyze what was read into the doc
System.Console.WriteLine($"{doc.Objects.Count} objects");
foreach(var obj in doc.Objects)
    var bbox = obj.Geometry.GetBoundingBox(true);
    System.Console.WriteLine($"Center is {bbox.Center}");

// done with the doc, free up the memory it is using
import Rhino
import os

doc = Rhino.RhinoDoc.CreateHeadless(None)
options = Rhino.FileIO.FileSkpReadOptions()
options.ImportCurves = False
options.EmbedTexturesInModel = False

path = os.path.expanduser("~/Desktop/test.skp")
doc.Import(path, options.ToDictionary())

print(f"{doc.Objects.Count} objects")
for obj in doc.Objects:
    bbox = obj.Geometry.GetBoundingBox(True)
    print(f"Center is {bbox.Center}")


Using Grasshopper with Code Driven

When using Grasshopper to read or write files, there are some differences then the above examples. The main difference is Grasshopper is it own document. When reading in a file, objects are then processed thru Grasshopper independantly. And when writing out a file format, then a headless doc needs to be created in the same component that writes the file. See the examples below:

Reading in a file format can be done in new Rhino 8 components using the Import File component:


Download Example Grasshopper Definition for Exporting a File

Here the file is imported and the objects can then be processed.

To write out any of the File formats from Rhino, create headless Doc and then add objects to it. The example below writes a 3MF file using a Python 3 component. Set the script component objects input to List Access and GeometryBase Input Hint:

import System
import Rhino

doc = Rhino.RhinoDoc.CreateHeadless(None)

if not objects is None:
    for o in objects:

options = Rhino.FileIO.File3mfWriteOptions()
options.Title = title
options.Designer = Rhino.RhinoApp.LicenseUserName
options.Metadata["Test"] = "Star"
options.MoveOutputToPositiveXYZOctant = True;

if not path is None:
    path = System.Environment.ExpandEnvironmentVariables(path)
    success = doc.Export(path, options.ToDictionary())

Other Use Cases

  • Batch processing: a directory of files could be read into headless documents and exported to another format.
  • Grasshopper: Using headless documents in Grasshopper with all Rhino file formats supports allows for processing geometry from different files without forcing the main Rhino active doc to change.