[RhinoCommon] File3dmObject UserDictionary content not available in RhinoDoc

When I write a File3dm that has geometry in it with content in its UserDictionary, this content is not available when that file is subsequently opened in Rhino.

To reproduce follow the following two steps:

  1. draw a plane or other surface
  2. run the first command as below. This will save test.3dm on your desktop
  3. open the written test.3dm file
  4. run the second command on the geometry in the opened file. It will show the user dictionary to be empty.

Please let me know if this is by design (I suspect it is not?) and how I can write into the UserDictionary such that it is available in Rhino.

Rhino 5SR7 by the way.

Command to write a file with an object with user dictionary info.

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
    ObjRef srfRef;

    Result res = RhinoGet.GetOneObject("Select surface to write", false, ObjectType.Surface, out srfRef);
    if (res != Result.Success) return res;

    Surface s = srfRef.Surface();
    s.UserDictionary.Set("MY_KEY", "MY STRING CONTENT");
    File3dm f = new File3dm();
    f.Polish();
    Guid g = f.Objects.AddSurface(s.ToNurbsSurface());
    File3dmObject obj = f.Objects.FirstOrDefault(o => o.Attributes.ObjectId == g);
    if (null == obj)
    {
        RhinoApp.WriteLine("Object not found.");
        return Result.Failure;
    }

    String found = obj.Geometry.UserDictionary.GetString("MY_KEY", "NOT FOUND");
    RhinoApp.WriteLine(found+" was found in the user dictionary");

    if (found == "NOT FOUND")
    {
        RhinoApp.WriteLine("User dictionary content not taken from geometry. Setting it directly on the object.");
        obj.Geometry.UserDictionary.Set("MY_KEY", "MY STRING CONTENT");
        String found2 = obj.Geometry.UserDictionary.GetString("MY_KEY", "NOT FOUND");
        RhinoApp.WriteLine(found2 + " was found in the user dictionary");
    }

    // put the written file on the desktop for easy reference
    String tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.3dm");
    bool written = f.Write(tmp, 5);
    if (!written)
    {
        RhinoApp.WriteLine("Unable to write file");
        return Result.Failure;
    }

    File3dm read = File3dm.Read(tmp);
    File3dmObject obj2 = read.Objects.FirstOrDefault(o => o.Attributes.ObjectId == g);
    if (null == obj2)
    {
        RhinoApp.WriteLine("Object not found in written file");
        return Result.Failure;
    }

    String found3 = obj2.Geometry.UserDictionary.GetString("MY_KEY", "NOT FOUND");
    RhinoApp.WriteLine(found3 + " was found in the read user dictionary");

    // so far so good. The content is in the written object's user dictionary.             

    return Result.Success;
}

The user dictionary can be inspected with this command

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
    ObjRef objRef;
    RhinoGet.GetOneObject("Select object", true, ObjectType.AnyObject, out objRef);

    if (null == objRef)
        return Result.Failure;

    RhinoObject obj = objRef.Object();
    GeometryBase geo = obj.Geometry;
    if (null == geo)
        return Result.Failure;
    RhinoApp.WriteLine("GUID: {0}",objRef.ObjectId);
    foreach (string key in geo.UserDictionary.Keys)
    {
        RhinoApp.WriteLine(key+" => {0}",geo.UserDictionary[key]);
    }

    if (geo.UserDictionary.Count == 0)
        RhinoApp.WriteLine("Empty");

    return Result.Success;
}

When run on the content of the written file, you will find there is no content in the object’s user dictionary.

When I run your first block of code in an SR8-ish build, I get the following output:

Select surface to write:
NOT FOUND was found in the user dictionary
User dictionary content not taken from geometry. Setting it directly on the object.
MY STRING CONTENT was found in the user dictionary
MY STRING CONTENT was found in the read user dictionary

'Is this not what you are getting?

It is what I get in SR7 too. There is a second part to this, that shows the problem.

You will have a test.3dm file on your desktop after running the first command. Open it and run the second command in my original post on the geometry inside it. The user dictionary of the object is empty.

Basically, it boils down to this:

If I read the file using File3dm.Read and query the object when it is of File3dmObject type I see the content of the UserDIctionary. If I subsequently load the file in Rhino, I get a reference to a RhinoDoc document. If I querye the object when it is of RhinoObject type, I do not see the content of the UserDIctionary.

Keep in mind that Rhino does not have a surface object. All surfaces and polysurfaces are represented by a Brep object.

When you do this:

Surface s = srfRef.Surface();

You are asking for the underlying surface from a Brep object. This is where you attached the dictionary.

When, you do this:

GeometryBase geo = obj.Geometry;

This returns, in this case, a Brep. There is no dictionary on the Brep itself, but rather on one of it’s surface members. If you change this code to this:

GeometryBase geo = obj.Surface();

The code will work

Does this help?

@dale that totally makes sense. When I change the first command to use a Brep rather than the surface, I also don’t need to set the information again.

It also explains why I had so much problems getting stuff into the user dictionary in the past.

Thanks, this was a real eye opener :smile: