Glue API Web and JS - Follow up
JSON to C# Class Generator

Creating a Merged Model Using Glue API

This question came from Joe Augustino through a forum post. I finally managed to clean up a sample for a blog post.

Q. I'm having trouble creating a merged model. I tried it using the Test Harness, but it is returning "Bad Request" each time. I can manually create it in Glue UI.  Can someone please check on this and verify?

A. I can confirm that the one in TestHarness does not work. It’s not up to date. We need clarification on this.      

According to the documentation: 
https://b4.autodesk.com/api/model/v1/merge/doc

Following are URL, request method, and required parameters.

Model Merge - Request  

URL: https://b4.autodesk.com/api/model/v1/merge.{format}

Supported Request Methods: POST

Required Parameters (here I'm skipping common sign-in parameters):

  • project_id
  • model_name
  • model_transformations

project_id is the project identifier that hold the model. model_name is the name of the merged model being created. 

Now, the big question mark is the third one, model_transformations. Here is what's written in the reference guide: 

"model_transformations: This is an array of JSON elements that represents all models that you want to merge. Each element contains model Id and transformation information for the model. If any of the sub-models are invalid or are merged models themselves, you will receive the HTTP response: 400 Bad Request
Values: Glue Model transformation format string in JSON format
Required: Yes"

Obviously, we need more explanation about the format of model_transformations.

In the following, I'm going to use Glue API Intro Labs2 as the base in building a sample.  

model_transformations

Below is a description of the object (modelTransformationRequests) that need to be serialized into JSON for the model_transformations string:

Description of model_transformations 
    List<model_units_and_transformation> modelTransformationRequests =
        new List<model_units_and_transformation>();
  
    [Serializable]
    public class model_units_and_transformation
    {
        public string Media { get; set; }
        public model_transformation_info Transform { get; set; }
        public string Type { get; set; }
        public string Units { get; set; }
        public int Version { get; set; }
    }

    [Serializable]
    public class model_transformation_info
    {
        public double[] Rotation { get; set; }
        public double[] Scale { get; set; }
        public double[] Translation { get; set; }
        public string Type { get; set; }
        public int Version { get; set; }
    }

where Media is model_version_id, which you can parse from the response from, for example, /model/v2/info request. We'll come to model/info later.   

Model Merge - Sample Code 

Given a model_transformations string, you can define Merge() or model/v1/merge call as follows: 

Merge
    public string Merge(string authToken, string projectId, string modelName, string modelTransformations)
    {
        string timeStamp = Utils.GetUNIXEpochTimestamp().ToString();
        string signature = Utils.ComputeMD5Hash(apiKey + apiSecret + timeStamp);

        // (1) Build request
        // set base url and authenticatopm info.
        var client = new RestClient();
        client.BaseUrl = baseApiUrl;

        // Set resource or end point
        var request = new RestRequest();
        request.Resource = "model/v1/merge.json";
        request.Method = Method.POST;

        // Add parameters
        request.AddParameter("company_id", companyId);
        request.AddParameter("api_key", apiKey);
        request.AddParameter("timestamp", timeStamp);
        request.AddParameter("sig", signature);
        request.AddParameter("auth_token", authToken);

        request.AddParameter("project_id", projectId);
        request.AddParameter("model_name", modelName);
        request.AddParameter("model_transformations", modelTransformations);

        // MH: Test merge to a specific folder.
        // This gets error. Need to be looked at.  
        //request.AddParameter("dest_folder_id", "xxx");

        // (2) Execute request and get response
        IRestResponse response = client.Execute(request);

        // Save response. This is to see the response for our learning.
        m_lastResponse = response;

        if (response.StatusCode != HttpStatusCode.OK)
        {
            return null;
        }

        return"OK";
    }

UI portion of the code might look something like below. Notice setupMergeParam() function, where we define model_transformations JSON string. We'll take a look how to obtain model_version_id later. For the time being, let's assume two model_version_id's are defined 

buttonMerge_Click, setupMergeParam, initTransformation
    // Merge two models
    private void buttonMerge_Click(object sender, EventArgs e)
    {
        string modelName = textBoxMergedModelName.Text;

        // Set up model_transformations parameter
        string modelTransformations = setupMergeParam(m_model_version_id1, m_model_version_id2);
        textBoxRequest.Text = modelTransformations;

        // Merge two models
        string test = glueCall.Merge(m_authToken, m_project_id, modelName, modelTransformations);
    }

    // Composing model transformations here
    private string setupMergeParam(string model1, string model2)
    {
        List<model_units_and_transformation> modelTransformationRequests =
            new List<model_units_and_transformation>();

        // Set trans info for model1
        model_units_and_transformation item1 = new model_units_and_transformation();
        item1 = initTransformation(item1);
        item1.Media = model1;
        modelTransformationRequests.Add(item1);

        // Set trans info for model2
        model_units_and_transformation item2 = new model_units_and_transformation();
        item2 = initTransformation(item2);
        item2.Media = model2;
        modelTransformationRequests.Add(item2);

        // Conver to JSON
        JsonSerializer serial = new JsonSerializer();
        string transString = serial.Serialize(modelTransformationRequests);

        return transString;
    }

    // Set the initial value of model transformation
    private model_units_and_transformation initTransformation(model_units_and_transformation item)
    {
        item.Media = "";
        item.Transform = new model_transformation_info();
        item.Transform.Rotation = new double[3];
        item.Transform.Rotation[0] = 90.0;
        item.Transform.Rotation[1] = 0.0;
        item.Transform.Rotation[2] = 0.0;
        item.Transform.Scale = new double[3];
        item.Transform.Scale[0] = 1.0;
        item.Transform.Scale[1] = 1.0;
        item.Transform.Scale[2] = 1.0;
        item.Transform.Translation = new double[3];
        item.Transform.Translation[0] = 0.0;
        item.Transform.Translation[1] = 0.0;
        item.Transform.Translation[2] = 0.0;
        item.Transform.Type = "ModelInstance";
        item.Transform.Version = 0;
        item.Type = "ModelInstance";
        item.Units = "Millimeter";
        item.Version = 1;

        return item;
    }

Note: for clarity of the main points, I have omitted the part that are intended for learning purpose from the above code, such as display of request and response. But it is included in the attached sample code.

Let's take a look at how to obtain model_version_id's next. 

Model Info V2 - Request  

model/v2/info call returns information of a given model id.  

URL: https://b4.autodesk.com/api/model/v2/info.{format}

Supported Request Methods: GET

Required Parameters (here I'm skipping common sign-in parameters):

  • model_id

Doc: https://b4.autodesk.com/api/model/v2/info/doc

 

Model Info V2 - Response 

model/v2/info returns a long list of infomation. Please refer to the documentation page for the full sample listing. Here we show the excerpt from it to focus on the item that we are interested parsing in our context: 

{

"version_history":[ ... ],
"action_id": "The action identifier used with the Display Component to view this Model",
"company_id": "The company identifier for this model",
"project_id": "The Project identifier for this model",
"model_id": "The master identifier for the model",
"model_version": 1,
"model_version_id": "The version identifier for this specific version of the model",
"model_name": "The name for the model",
"created_by": "The login_name of the creator of the model",
"created_by_first_name": "First name of the creator of the model",
"created_by_last_name": "Last name of the creator of the model",
"created_date": "The date the model was created",
"modified_by": "The login_name of the last user to modify the model",
"modified_date": "Date of last modification",
"parent_folder_id": "The parent folder for the model",
"file_parsed_status": 1,
"is_merged_model": 1,
"merged_model_available": 0,

... 

}

For our purpose of merging models, we are interested in model_version_id

 

Model Info V2 - Sample Code 

Here is the sample code for ModelInfoV2() which calls model/v2/info: 

ModelInfoV2
    public string ModelInfoV2(string authToken, string modelId)
    {
        string timeStamp = Utils.GetUNIXEpochTimestamp().ToString();
        string signature = Utils.ComputeMD5Hash(apiKey + apiSecret + timeStamp);

        // (1) Build request
        // set base url and authenticatopm info.
        var client = new RestClient();
        client.BaseUrl = baseApiUrl;

        // Set resource or end point
        var request = new RestRequest();
        request.Resource = "model/v2/info.json";
        request.Method = Method.GET;

        // Add parameters
        request.AddParameter("company_id", companyId);
        request.AddParameter("api_key", apiKey);
        request.AddParameter("timestamp", timeStamp);
        request.AddParameter("sig", signature);
        request.AddParameter("auth_token", authToken);

        request.AddParameter("model_id", modelId);

        // (2) Execute request and get response
        IRestResponse response = client.Execute(request);

        // Save response. This is to see the response for our learning.
        m_lastResponse = response;

        if (response.StatusCode != HttpStatusCode.OK)
        {
            return null;
        }

        // Get a model version id.
        JsonDeserializer deserial = new JsonDeserializer();
        ModelInfo modelInfoResponse =
            deserial.Deserialize<ModelInfo>(response);
        string model_version_id = modelInfoResponse.model_version_id;

        return model_version_id;
    }

UI portion for Model Info v2 might look something like below: 

buttonModelInfo1_click, buttonModelInfo2.click
    // Two model ids to merge test.
    private static string m_model_version_id1 = "";
    private static string m_model_version_id2 = "";

    // Get the model version id of the first model
    private void buttonModelInfo1_Click(object sender, EventArgs e)
    {
        m_model_version_id1 = glueCall.ModelInfoV2(m_authToken, m_model_id);
    }

    // Get the model version id of the second model
    private void buttonModelInfo2_Click(object sender, EventArgs e)
    {
        m_model_version_id2 = glueCall.ModelInfoV2(m_authToken, m_model_id);
    }

Once again, for clarity of the main points, I have omitted parts, such as display of request and response. But it is included in the attached sample code.

Putting Together 

After putting together the above code, the image of UI of the test app looks like this. 

   GlueAPIMergeDialog2

I have attached the sample project I have used here. Download 2 GlueAPIIntro_MergeTest

To test using this program, 

  1. [Login] using Glue credentials. 
  2. Use [Projects] and [Models] button till you find a right project and model to merge. By clicking the buttons, it increments the index of current project and model.  
  3. When you see a right model to merge, select it by pressing [Model Info1] button.
  4. Repeat to set the second model.
  5. Finally press [Merge] button to execute merge.
  6. Go to the Glue UI and see if you see a merged model.   

A little caveat: currently, we cannot specify the folder to merge. Specifying dest_folder_id does not work. You can work around by moving the model after it is created.

Many thanks to Joe for bringing up this topic, help testing it and suggesting the workaround.

Mikako 

Comments