Geometry in wrong location when accessing in API

Geometry in wrong location when accessing in API

Anonymous
Not applicable
8,330 Views
14 Replies
Message 1 of 15

Geometry in wrong location when accessing in API

Anonymous
Not applicable

I am using the API in an RVM file to access the geometry and coordinates and some of the elements are in the wrong location- offset from where they should be. I use the same bit of code for all th1.jpge geometry using the method in the following post https://forums.autodesk.com/t5/navisworks-api/get-global-coordinates-of-the-points-of-the-triangle/m... which works for most elements . The blue shows what it is supposed to look like,

 

and beloew is where the pipe comes out according to the api and converted coordinates.

 

2.jpg

I have uploaded  the sample rvm file as well https://drive.google.com/file/d/0B06YsaeeROzjZkdEdGZmRkozS0E/view?usp=sharing

0 Likes
Accepted solutions (1)
8,331 Views
14 Replies
Replies (14)
Message 2 of 15

xiaodong_liang
Autodesk Support
Autodesk Support

Hi,

 

I tried to dump the primitives of this model by API. It looks there are huge number of triangles with this pipe only (the data file is 2G bytes). I cannot even proceed to test these dataset in my own WebGL harness.


Could you share more information? It will be of more helpful if you could provide the code snippet how to use these triangles.

 

The following is the theory on the traingles that Navisworks API returned. Since you mentioned your code works well with other model, I think you would have followed this theory. Anyway, I am enclosing in case of need.

 

>>>>>>>>>>>>>>>

The primitives (triangles) Navisworks dumps are triangle strip. So they are connected triangles. The indices of faces are exactly the sequences of these vertices one by one. i.e.assume you got the triangles list from Navisworks API:


vertices=[
[*,*,*],
[*,*,*],
[*,*,*],
[*,*,*],
[*,*,*],
[*,*,*],
[*,*,*],
......
]

you just need to build the face with the indices (e.g. in Three.js)


****************
var index = 0;
for( var i = 0; i < vertices.length - 3; i++ ){
geometry.faces.push( new THREE.Face3( index, index+1, index+2 ));
index = index +1;
}

***********************

 


 

 

0 Likes
Message 3 of 15

Anonymous
Not applicable

 

Using the info in the previous post I convert the coordinates using bounding box to shift them the the world. Is it possible for bounding boxes to be incorrect ? is there a way through api to have it recomputed?  if just looks like the pipe is shifted.

 

I realize there are a lot triangles (trust me these large files are not quick to process).

 

Here is my code to do the point conversion of triangles.

 

public void Triangle( ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2, ComApi.InwSimpleVertex v3 )

{

 

try

{

Array arrayV1 = (Array)(object)v1.coord;

Array arrayV2 = (Array)(object)v2.coord;

Array arrayV3 = (Array)(object)v3.coord;

Point3D pnt1 = new Point3D((float)arrayV1.GetValue( 1 ) + _currentItem.Geometry.BoundingBox.Center.X, (float)arrayV1.GetValue( 2 ) + _currentItem.Geometry.BoundingBox.Center.Y,

(float)arrayV1.GetValue( 3 ) + _currentItem.Geometry.BoundingBox.Center.Z);

 

Point3D pnt2 = new Point3D((float)arrayV2.GetValue( 1 ) + _currentItem.Geometry.BoundingBox.Center.X, (float)arrayV2.GetValue( 2 ) + _currentItem.Geometry.BoundingBox.Center.Y,

(float)arrayV2.GetValue( 3 ) + _currentItem.Geometry.BoundingBox.Center.Z);

 

Point3D pnt3 = new Point3D((float)arrayV3.GetValue( 1 ) + _currentItem.Geometry.BoundingBox.Center.X, (float)arrayV3.GetValue( 2 ) + _currentItem.Geometry.BoundingBox.Center.Y,

(float)arrayV3.GetValue( 3 ) + _currentItem.Geometry.BoundingBox.Center.Z);

 

 

thanks for your reply

 

0 Likes
Message 4 of 15

xiaodong_liang
Autodesk Support
Autodesk Support

Sorry I was very busy on DevDays + DevWeek China.

firstly, I had not clue to reproduce your problem at my side, so I was suggesting to take a look how you used those triangles data. It is not the code of Navisworks API. What I wanted to know is your graphics workflow. e.g. at my side, I simply used the data in an HTML webpage based on Three.js.

I think we can put the global/local transformation aside firstly. instead,  if we re-build the graphics with the triangles data only, the relevant coordinates would at least present the correct relationship of the sections. I do not think it is related to bundingbox.

In addition, is it possible for you to simplify the source model a little? it is strange it has so many triangles. I will need to reproduce it at my side, but it always crashed to dump the huge triangles.

in the same time, I will also consult with our engineer team.

0 Likes
Message 5 of 15

xiaodong_liang
Autodesk Support
Autodesk Support
Accepted solution

This is an update about this case:

 

 

I had private discussion with jay.vose. After some tests, the question is just about LCS to WCS which has been indicated in the blog:

http://adndevblog.typepad.com/aec/2012/05/get-primitive-from-solid-of-navisworks.html

 

Jay happened to use other way to transform the coordinates. The below is what I tested with, though not written elegantly . Jay replied it can solve his problem. I am enclosing for reference.

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

 
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;

using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
 

namespace Lab_COM_Interop
{ 
    #region "Plug-in Dump Geometry Primitives"
    [PluginAttribute("NWAPI_Dump_Primitives", //Plugin name
                      "ADSK", //Developer ID or GUID
                      ToolTip = "Dump Geometry Primitives of Model Item",
        //The tooltip for the item in the ribbon
                      DisplayName = "Dump Geometry Primitives")]
    //Display name for the Plugin in the Ribbon
    public class NWAPI_Dump_Primitives : AddInPlugin
    {
        public override int Execute(params string[] parameters)
        {
            // get the current selection
            ModelItemCollection oModelColl = Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems;

            //convert to COM selection
            ComApi.InwOpState oState = ComApiBridge.State;
            ComApi.InwOpSelection oSel = ComApiBridge.ToInwOpSelection(oModelColl);

            // create the callback object
            CallbackGeomListener callbkListener = new CallbackGeomListener();
            foreach (ComApi.InwOaPath3 path in oSel.Paths())
            {
                foreach (ComApi.InwOaFragment3 frag in path.Fragments())
                {

                    ComApi.InwLTransform3f3 localToWorld = (ComApi.InwLTransform3f3)(object)frag.GetLocalToWorldMatrix();
                    //Array array_v1 = (Array)(object)localToWorld.Matrix;

                    callbkListener.LCS2WCS = localToWorld;

                    // generate the primitives
                    frag.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, callbkListener);


                }
            }
             
            return 0;
        }

    }

    #region InwSimplePrimitivesCB Class
    class CallbackGeomListener : ComApi.InwSimplePrimitivesCB
    {
        static FileStream aFile = new FileStream("c:\\temp\\NavisworksObjGeomtries.txt", FileMode.OpenOrCreate);
        static StreamWriter sw = new StreamWriter(aFile);
        public ComApi.InwLTransform3f3 LCS2WCS;
        public void Line(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2)
        {
            // do your work
        }

        public void Point(ComApi.InwSimpleVertex v1)
        {
            // do your work
        }

        public void SnapPoint(ComApi.InwSimpleVertex v1)
        {
            // do your work
        }

        public void Triangle(ComApi.InwSimpleVertex v1,
                ComApi.InwSimpleVertex v2,
                ComApi.InwSimpleVertex v3)
        {
            // do your work


            Array array_v1 = (Array)(object)v1.coord;
            Array array_v2 = (Array)(object)v2.coord;
            Array array_v3 = (Array)(object)v3.coord;

            double xTrans = LCS2WCS.GetTranslation().data1;
            double yTrans = LCS2WCS.GetTranslation().data2;
            double zTrans = LCS2WCS.GetTranslation().data3;

            double v1_x = Convert.ToDouble(array_v1.GetValue(1));

            StringBuilder oStr = new StringBuilder();
            oStr.Append("[" + (Convert.ToDouble(array_v1.GetValue(1)) + xTrans).ToString() + "," +
                              (Convert.ToDouble(array_v1.GetValue(2)) + yTrans).ToString() + "," +
                              (Convert.ToDouble(array_v1.GetValue(3)) + zTrans).ToString() + "]");
            oStr.Append("[" + (Convert.ToDouble(array_v2.GetValue(1)) + xTrans).ToString() + "," +
                              (Convert.ToDouble(array_v2.GetValue(2)) + yTrans).ToString() + "," +
                              (Convert.ToDouble(array_v2.GetValue(3)) + zTrans).ToString() + "]");
            oStr.Append("[" + (Convert.ToDouble(array_v3.GetValue(1)) + xTrans).ToString() + "," +
                             (Convert.ToDouble(array_v3.GetValue(2)) + yTrans).ToString() + "," +
                              (Convert.ToDouble(array_v3.GetValue(3)) + zTrans).ToString() + "]\n");

            sw.WriteLine(oStr);

        }
        ~CallbackGeomListener()
        {
            sw.Close();
        }

    }
    #endregion


    #endregion


  

}

 

 

 

 

0 Likes
Message 6 of 15

mycad2016D8W4P
Enthusiast
Enthusiast

@xiaodong_liang, @Anonymous,

What about if there are rotations not only translations on the original model geometry? 

Is the InwLTransform3f3.GetLinear() API not implemented?

 

Thanks

Message 7 of 15

mycad2016D8W4P
Enthusiast
Enthusiast

@xiaodong_liang, @Anonymous,

Another question,Are the primitives generated in high quality or low quality? Is it able to control the quality (triangle count) generated through Navsworks API?

 

Thanks.

Message 8 of 15

kinjal
Enthusiast
Enthusiast

I've exactly same question.. how about ModelItems having rotation applied as well?

Kinjal Desai
Fullstack developer @ Dwaravati
Delivering high quality programmatic boosts for your already beautiful Revit


0 Likes
Message 9 of 15

mechanicalsdsCPTEF
Explorer
Explorer

The solution provided helped me with translation, but I (like a couple others who have replied) was also having issues with some geometry being scaled and rotated incorrectly.  Ultimately, I found that using Matrix instead of GetTranslation resulted in the complete translation of the geometry.  I modified the code as shown below to achieve this.

 

foreach (InwOaFragment3 fragment in path.Fragments())
{

    var localToWorld = (InwLTransform3f3)(object)fragment.GetLocalToWorldMatrix();

var localToWorldMatrix = (Array)(object)localToWorld.Matrix; callbkListener.matrix = new System.Windows.Media.Media3D.Matrix3D(); matrix.M11 = (double)localToWorldMatrix.GetValue(1); matrix.M12 = (double)localToWorldMatrix.GetValue(2); matrix.M13 = (double)localToWorldMatrix.GetValue(3); matrix.M14 = (double)localToWorldMatrix.GetValue(4); matrix.M21 = (double)localToWorldMatrix.GetValue(5); matrix.M22 = (double)localToWorldMatrix.GetValue(6); matrix.M23 = (double)localToWorldMatrix.GetValue(7); matrix.M24 = (double)localToWorldMatrix.GetValue(8); matrix.M31 = (double)localToWorldMatrix.GetValue(9); matrix.M32 = (double)localToWorldMatrix.GetValue(10); matrix.M33 = (double)localToWorldMatrix.GetValue(11); matrix.M34 = (double)localToWorldMatrix.GetValue(12); matrix.OffsetX = (double)localToWorldMatrix.GetValue(13); matrix.OffsetY = (double)localToWorldMatrix.GetValue(14); matrix.OffsetZ = (double)localToWorldMatrix.GetValue(15); matrix.M44 = (double)localToWorldMatrix.GetValue(16); fragment.GenerateSimplePrimitives(nwEVertexProperty.eNORMAL, callbkListener); }

 

 

 

public void Triangle(InwSimpleVertex v1, InwSimpleVertex v2, InwSimpleVertex v3)
{
    var points = new System.Windows.Media.Media3D.Point3D[3];

    Array array_v1 = (Array)(object)v1.coord;
    Array array_v2 = (Array)(object)v2.coord;
    Array array_v3 = (Array)(object)v3.coord;

    points[0].X = Convert.ToDouble(array_v1.GetValue(1));
    points[0].Y = Convert.ToDouble(array_v1.GetValue(2));
    points[0].Z = Convert.ToDouble(array_v1.GetValue(3));

    points[1].X = Convert.ToDouble(array_v2.GetValue(1));
    points[1].Y = Convert.ToDouble(array_v2.GetValue(2));
    points[1].Z = Convert.ToDouble(array_v2.GetValue(3));

    points[2].X = Convert.ToDouble(array_v3.GetValue(1));
    points[2].Y = Convert.ToDouble(array_v3.GetValue(2));
    points[2].Z = Convert.ToDouble(array_v3.GetValue(3));

    matrix.Transform(points);

     //points array is now transformed by offset, scale and rotation

}

 

 

 

Message 10 of 15

awmcc90VZTT2
Contributor
Contributor

Just to add to the above:

 

InwLTransform3f3 is column major order whereas System.Windows.Media.Media3D.Matrix3D is row major order.

 

Something that would be more efficient would look like this:

 

 

public T[] ToArray<T>(Array arr)
{
    T[] result = new T[arr.Length];
    Array.Copy(arr, result, result.Length);
    return result;
}

foreach (InwOaFragment3 fragment in path.Fragments())
{

    var localToWorld = (InwLTransform3f3)(object)fragment.GetLocalToWorldMatrix();

    var localToWorldMatrix = (Array)(object)localToWorld.Matrix;
    Elements = ToArray<double>(localToWorldMatrix);
    ...
}

public float[] OfPoint(float x, float y, float z)
{
    float w = Elements[3] * x + Elements[7] * y + Elements[11] * z + Elements[15];
    return new float[] {
        (Elements[0] * x + Elements[4] * y + Elements[8] * z + Elements[12]) / w,
        (Elements[1] * x + Elements[5] * y + Elements[9] * z + Elements[13]) / w,
        (Elements[2] * x + Elements[6] * y + Elements[10] * z + Elements[14]) / w
        };
}

The values that come out of the InwSimpleVertex array are floating points. This performs the complete calculation as well as provides the most efficient method of converting an array to a normal 0-indexed array of a particular type.

 

*Edit: I just wanted to add that having profiled this operation many many times, <array>.GetValue(index) is an incredibly expensive operation in relation to Array.copy. Also, Array.copy doesn't care what values these arrays are indexed which will lead to less confusion and less error.

 

 

Message 11 of 15

Anonymous
Not applicable

What are people doing too fix the vertex normals in this situation?

0 Likes
Message 12 of 15

alexisDVJML
Collaborator
Collaborator

Thanks @awmcc90VZTT2 for this double whammy sample !


Not only you provide the way to apply the transformation matrix to get correct world coordinates, but also you a trick re: partially solving the frustrating low-performance of <array>.GetValue(index) !!!

Eager to integrate this idea in my code and be the one profiling (again ;->), another tedious process but critical to achieve reasonable performance for Plug-ins working on real world models...

Main Scientist, Full Stack Developer & When Time Permits Director of IDIGO ► On your marks, Set, Go
0 Likes
Message 13 of 15

winderson2GYXS
Contributor
Contributor

Can someone explain or post a code using @awmcc90VZTT2 solution?

 

I'm trying to implement here but I don't understand where to use the methods, or the Element class.

0 Likes
Message 14 of 15

572237540
Contributor
Contributor

Excuse me,How to get the matrix of LCS(local coordinate space) to parent coordinate space.
What's the use of ModelItem.Transform? Can I use it to convert vertices into WCS(world coordinate space)?

0 Likes
Message 15 of 15

dancleary21
Explorer
Explorer

Hi @awmcc90VZTT2 
Could you please share more of your solution code so that we can better understand it.


Where is "OfPoint" called? 
Where is "Elements" declared?

0 Likes