Eyeshot Proprietary File Format

Eyeshot 11 includes the new proprietary file format (versioned) with options to save geometry only, tessellation only or both (to leverage file size vs. loading speed). It also supports entity types extension and file compression.

This file format will always be able to read old version files and will save current version only. 

Serialization strategy

The Eyeshot file format is a binary format with a header and a body, based on the protobuf-net open source library. 

General concepts: 

  • Surrogate: defines the properties to store.
    Each Eyeshot's object has its surrogate and implements the method
    ConvertToSurrogate().
  • Model: contains the hierarchy's objects and defines the fields for each surrogate.
  • Serializer: performs the serialization/deserialization operations based on its model. 

Relevant classes: 

devDept.Serialization namespace

  • FileHeader: contains basic info like file version, content type1, serialization mode2, units, etc.
  • FileBody: contains all the Eyeshot's objects like entities, layers, blocks, etc.
  • FileSerializer3: the serializer for the Eyeshot proprietary file format. 

devDept.Eyeshot.Translators namespace

  • ReadFile: helper class to open a file.
  • WriteFile: helper class to save a file.

Code snippets

Open file

ReadFile readFile = new ReadFile(filename/*, new FileSerializerEx()*/); // If you need Autodesk objects, you have to reference the Eyeshot control x86 (or x64) assembly and pass the FileSerializerEx as parameter.
readFile.DoWork();
readFile.AddToScene(viewportLayout1);
viewportLayout1.Invalidate();

Save file

WriteFile writeFile = new WriteFile(new WriteFileParams(viewportLayout1), filename /*, new FileSerializerEx()*/); // If you need Autodesk objects, you have to pass the FileSerializerEx as parameter.
writeFile.DoWork();
1 geometry: includes only geometrical data, i.e. for a circle Plane and radius (smaller file size, but slower loading process).
  tessellation: includes only tessellation data, i.e. for a circle the vertices (loading the file, the circle will be represented as a linear path)
  geometry&tessellation: includes both geometrical and tessellation data, i.e. for a circle Plane, radius and vertices (bigger file size, but quickly loading process).

2Compressed or Uncompressed 

3To include also the Autodesk objects you have to use the FileSerializerEx. 

 

Extendibility

It can be easily extended with custom entities and custom data, you just need:

  • define surrogates for custom objects
  • create a derived FileSerializer3 class and make the override of the FillModel() method.
  • use the Tag field of the FileHeader to handle the custom objects version

A good start point is the FileFormatExtension source code sample.

using devDept.Serialization
   
public class CustomDataSurrogate : Surrogate {     public CustomDataSurrogate(CustomData obj) : base(obj) // The base calls the CopyDataFromObject method. { }
public int Id { get; set; } public string Description { get; set; } public float Price { get; set; }
protected override CustomData ConvertToObject() { CustomData cd = new CustomData(Id); CopyDataToObject(cd); return cd; }
protected override void CopyDataToObject(CustomData cd) { cd.Description = Description; if (Tag == "1.1") cd.Price = 100; // forces the price for old version 1.1.  } protected override void CopyDataFromObject(CustomData cd) { Id = cd.Id; Description = cd.Description; Price = cd.Price; }
// those static methods must be created only for classes that derive from the Surrogate base class, no for custom entity surrogate, i.e. MyCircleSurrogate  #region Static Methods public static implicit operator CustomData(CustomDataSurrogate surrogate) { return surrogate == null ? null : surrogate.ConvertToObject(); } public static implicit operator CustomDataSurrogate(CustomData source) { return source == null ? null : source.ConvertToSurrogate(); } #endregion }
public class MyFileSerializer : FileSerializer
{
    // Tag to handle custom versions.
    public static string CustomTag = "1.2";
    
    // Constructor used in conjunction with the WriteFile class.   
    public MyFileSerializer()
    {
    }
    
    // Constructor used in conjunction with the ReadFile class.
    public MyFileSerializer(contentType contentType) : base(contentType)
    {
    }   

    protected override void FillModel()
    {
        base.FillModel();            
     
        // Adds the CustomData to the protobuf model and defines its surrogate.
        Model.Add(typeof(CustomData), false)
            .SetSurrogate(typeof(CustomDataSurrogate));
    
        // Defines properties for CustomDataSurrogate
        MetaType mt = Model[typeof(CustomDataSurrogate)]
            .Add(1, "Id")                
            .Add(2, "Description");
        // Use the header tag to handle different definitions for your custom model. 
        if (this.HeaderTag == "1.2")        
            mt.Add(3, "Price");        
    
        mt.SetCallbacks(null, null, "BeforeDeserialize", null); // Fills Version and Tag during the deserialization.
        mt.UseConstructor = false; // Avoids to use the parameterless constructor during the serialization process.
    }
}
Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

Comments

2 comments
  • I currently use BinaryFormatter. This is very inefficient compared to Protobuffers. But BinaryFormatter is so easy to use since the class description is included in the file. I would like to switch to Protobuffers for so many reasons.

    In this post, it looks like it is required to make a surrogate class for each of my 'real' classes. This is painful because I have 100s of my classes. Is there some automated way to do this?

    https://github.com/mgravell/protobuf-net - In this project, they use attributes. Even this is time consuming. They also have a method of creating a .proto file from the C# source that contains the integer/property definitions. Then the C# code does not require any changes. Maybe this would not work for you since you are deciding what to serialize at runtime (ie: tessellation)

    Also, does this support reference map serialization? A great feature of BinaryFormatter is it contains a reference map. So if you serialize circular references, or the same object multiple times, it deserializes exactly the same. I'm sure protobuffers does not by default, but there maybe an options.

    I guess it is not possible to serialize Eyeshot objects with other protobuf libraries such as the protobuf-net library above? I would prefer to use your system directly.

    -Jas

  • Hi James,

    protobuf-net supports the attributes definition but we use the surrogates to keep a clear separation between the Eyeshot objects definition and the one used for the serialization purpose. In this way, we can better handle versioning and overcome some limits about unsupported types in protobuf-net, like object, enum, multi-dimensional array, etc. Moreover, as you wrote, we can decide what to serialize at runtime according to the content type.

    Regarding the serialization of circular references, you can search on the web, this is one of the first result: https://stackoverflow.com/questions/7185632/understanding-protobuf-net-asreference-with-recursive-referencing

Please sign in to leave a comment.