RhinoDoc.CreateHeadless duplicates template viewports and SaveAs

RhinoDoc.CreateHeadless duplicates template viewports and SaveAs writes an invalid ActiveViewId

I think I’ve found a Rhino 9 WIP issue involving RhinoDoc.CreateHeadless() and viewport state.

When I create a headless document from a 3DM template containing the standard four viewports, the resulting document contains eight viewports. After saving it, the output file still contains eight viewports, and its ActiveViewId does not match any saved viewport ID.

This means the active/maximized viewport from the template is not restored when the resulting file is opened normally.

This is with Rhino 9 WIP, RhinoCommon 9.0.26237.15343.

The CreateHeadless documentation says that the template initializes the new document. The lower-level document-creation documentation also says that default views are created only when the template does not contain views: CRhinoCreateDocumentOptions.

To reproduce, first save a normal empty Rhino document with its standard four viewports as C:\Temp\template.3dm, then run this in the Python 3 Script Editor:

import os
import tempfile
import Rhino

folder = os.path.join(tempfile.gettempdir(), "RhinoHeadlessViewportRepro")
template_path = os.path.join(folder, "template.3dm")
output_path = os.path.join(folder, "output.3dm")
os.makedirs(folder, exist_ok=True)


def print_doc(label, doc):
    views = list(doc.Views.GetStandardRhinoViews())
    active = doc.Views.ActiveView
    active_id = active.ActiveViewport.Id if active else None
    ids = [view.ActiveViewport.Id for view in views]
    print(label)
    print("count:", len(views))
    print("active:", active_id)
    print("active present:", active_id in ids)
    for view in views:
        print(view.ActiveViewport.Name, view.ActiveViewport.Id, view.Maximized)


def print_file(label, path):
    model = Rhino.FileIO.File3dm.Read(path)
    try:
        ids = [view.Viewport.Id for view in model.Views]
        print(label)
        print("count:", model.Views.Count)
        print("active:", model.Settings.ActiveViewId)
        print("active present:", model.Settings.ActiveViewId in ids)
        for view in model.Views:
            print(view.Name, view.Viewport.Id, view.Maximized)
    finally:
        model.Dispose()


source = Rhino.RhinoDoc.ActiveDoc
template = Rhino.FileIO.File3dm()
top_id = None

for source_view in source.Views.GetStandardRhinoViews():
    view = Rhino.DocObjects.ViewInfo(source_view.ActiveViewport)
    is_top = source_view.ActiveViewport.Name == "Top"
    view.Maximized = is_top
    template.Views.Add(view)
    if is_top:
        top_id = view.Viewport.Id

if top_id is None:
    raise RuntimeError("The current document has no Top viewport.")

template.Settings.ActiveViewId = top_id
print("template saved:", template.Write(template_path, Rhino.RhinoApp.ExeVersion))
template.Dispose()

print_file("template file", template_path)

doc = Rhino.RhinoDoc.CreateHeadless(template_path)
try:
    print_doc("headless document", doc)
    print("output saved:", doc.SaveAs(output_path))
finally:
    doc.Dispose()

print_file("saved file", output_path)
print("folder:", folder)

In my test:

  • The template contains four viewports.
  • The headless document contains eight.
  • The saved file contains eight.
  • The saved ActiveViewId does not match any viewport in the file.

As a workaround, I reopen the saved file using File3dm, set Settings.ActiveViewId to the intended Top viewport ID, mark that viewport as maximized, and write the file again.

Does this look like a Rhino bug, or is there a different API that should be used to preserve the template’s viewport state in a headless document?

Hi @Stefano_Menci,

I’ve logged an issue so we can look into this.

– Dale