Why this code fail to properly convert PolyFaceMesh to TinSurface, and how to fix it?

soonhui
Collaborator
Collaborator

Why this code fail to properly convert PolyFaceMesh to TinSurface, and how to fix it?

soonhui
Collaborator
Collaborator

NOTE:

 

Based on the communication with Civil 3D developer, I can confirm that the problem in the original test case is a bug

 

A fix is underway, but don't know when will come. 

 

Problem description:

 

I have the following code that works very well when I convert from PolyFaceMesh to TinSurface under simple situation (eg: if I have a L shape PolyFaceMesh, then the TinSurface converted will preserve the PolyFaceMesh).

 

 

 

        [CommandMethod(nameof(ConvertFromMeshToTinSurface))]
        public void ConvertFromMeshToTinSurface()
        {
            using (var tsDest = ActiveACADDocument.TransactionManager.StartTransaction())
            {
                var destBlockTable = tsDest.GetObject(ActiveACADDocument.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
                var destinationModelSpace = tsDest.GetObject(destBlockTable[BlockTableRecord.ModelSpace],
                    OpenMode.ForWrite) as BlockTableRecord;
                foreach (var id in destinationModelSpace)
                {
                    var entity = tsDest.GetObject(id, OpenMode.ForRead) as PolyFaceMesh;
                    if(entity== null)
                        continue;
                    var surfaceId2 = TinSurface.Create(ActiveACADDocument.Database, "FromMeshTest");
                    var platformSurface = tsDest.GetObject(surfaceId2, OpenMode.ForWrite) as TinSurface;
                    platformSurface.DrawingObjectsDefinition.AddFromPolyFaces(new ObjectIdCollection(new[] { entity.Id }),
                        true, $"Normal Platform");
                    var spuriousEdges = platformSurface.GetEdges().Where(ee => !ee.IsLocked).ToList(); //IsLocked indicates that it's not a part of the edge in PolyMesh

                    if (spuriousEdges.Count > 0)
                    {

                        platformSurface.DeleteLines(spuriousEdges);
                    }
                    platformSurface.Rebuild();
                    break;
                    //MessageBox.Show(platformSurface.GetTerrainProperties().SurfaceArea2D.ToString());


                }
                tsDest.Commit();
                ACADEditor.ZoomExtents();


            }

        }

 

 

 

However, for a slightly complicated drawing like the one I attach here, it seems that some face is not properly converted into TinSurface, as shown when you compare the two screenshots for the PolyFaceMesh object and the TinSurface object:

 

tinsurface.pngpolyfacemesh.png

 

Note that some face is missing in TinSurface as per above screenshot.

 

How can I fix this? Is there a robust way that guarantees 100% conversion from PolyFaceMesh to TinSurface?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes
Reply
Accepted solutions (1)
1,042 Views
16 Replies
Replies (16)

soonhui
Collaborator
Collaborator

Here's another (similar) drawing, and the problem looks even worse:

 

polyfacemesh2.pngtinsurface2.png

 

I'm unsure whether this is a bug, or just my code of converting from  PolyFaceMesh to TinSurface is not robust.

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

Jeff_M
Consultant
Consultant

This:

var spuriousEdges = platformSurface.GetEdges().Where(ee => !ee.IsLocked).ToList();

looks like it must be returning edges that you really dont want returned. I think you will need to go through the FaceRecords of the PolyFaceMesh and save a list of the edges which are not visible, then remove those edges from the surface. Not sure of the best/easiest way to do this, perhaps find the midpoint of the non-visible edges then use the TinSurface.FindEdgeAtXY() to get the surface edges to remove.

Jeff_M, also a frequent Swamper
EESignature
0 Likes

soonhui
Collaborator
Collaborator

OK, I can reproduce the problem using a very simple code ( with no complicated geometry from another drawing).

 

This is just a L shape PolyFaceMesh, and yet I can reproduce the problem.

 

smaller L shape example.png

 

Here's the code:

 

        [CommandMethod(nameof(LShapeAddFromPolyFace))]
        public void LShapeAddFromPolyFace()
        {
            try
            {
                using (var ts = ActiveACADDocument.TransactionManager.StartTransaction())
                {

                    var surfaceId2 = TinSurface.Create(ActiveACADDocument.Database, "FromMeshTest");
                    var platformSurface = ts.GetObject(surfaceId2, OpenMode.ForWrite) as TinSurface;

                    var bt = (BlockTable)ts.GetObject(ActiveACADDocument.Database.BlockTableId, OpenMode.ForRead, false);
                    var btr = (BlockTableRecord)ts.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false);

                    var vertices = platformSurface.Vertices;
                    if (vertices.Count != 0)
                    {
                        platformSurface.DeleteVertices(vertices);
                    }
                    var polyMesh = new PolyFaceMesh();
                    polyMesh.SetDatabaseDefaults();
                    btr.AppendEntity(polyMesh);
                    ts.AddNewlyCreatedDBObject(polyMesh, true);

                    var vertex1 = new PolyFaceMeshVertex(new Point3d(0, 0, 0));
                    polyMesh.AppendVertex(vertex1);
                    ts.AddNewlyCreatedDBObject(vertex1, true);
                    var vertex2 = new PolyFaceMeshVertex(new Point3d(10, 0, 0));
                    polyMesh.AppendVertex(vertex2);
                    ts.AddNewlyCreatedDBObject(vertex2, true);
                    var vertex3 = new PolyFaceMeshVertex(new Point3d(10, 10, 0));
                    polyMesh.AppendVertex(vertex3);
                    ts.AddNewlyCreatedDBObject(vertex3, true);
                    var vertex4 = new PolyFaceMeshVertex(new Point3d(0, 10, 0));
                    polyMesh.AppendVertex(vertex4);
                    ts.AddNewlyCreatedDBObject(vertex4, true);
                    var vertex5 =new PolyFaceMeshVertex(new Point3d(5, 10, 0));
                    polyMesh.AppendVertex(vertex5);
                    ts.AddNewlyCreatedDBObject(vertex5, true);
            
                 
                    var vertex6 = new PolyFaceMeshVertex(new Point3d(10, 10, 0));
                    polyMesh.AppendVertex(vertex6);
                    ts.AddNewlyCreatedDBObject(vertex6, true);
                 

                
                    var vertex7 =new PolyFaceMeshVertex(new Point3d(10, 20, 0));
                    polyMesh.AppendVertex(vertex7);
                    ts.AddNewlyCreatedDBObject(vertex7, true);
           

                    var vertex8 =new PolyFaceMeshVertex(new Point3d(5, 20, 0));
                    polyMesh.AppendVertex(vertex8);
                    ts.AddNewlyCreatedDBObject(vertex8, true);
               


                    var fr1 = new FaceRecord(1, 2, 3, 0);  //this is one based right? Furthermore, this is a triangle, so the last argument is 0. I hope my intepretation is correct!
                    polyMesh.AppendFaceRecord(fr1);
                    ts.AddNewlyCreatedDBObject(fr1, true);
                    var fr2 = new FaceRecord(1, 3, 4, 0);  //this is one based right? Furthermore, this is a triangle, so the last argument is 0. I hope my intepretation is correct!
                    polyMesh.AppendFaceRecord(fr2);
                    ts.AddNewlyCreatedDBObject(fr2, true);

                    var fr3 = new FaceRecord(5,6,7, 0);  //this is one based right? Furthermore, this is a triangle, so the last argument is 0. I hope my intepretation is correct!
                    polyMesh.AppendFaceRecord(fr3);
                    ts.AddNewlyCreatedDBObject(fr3, true);

                    var fr4 = new FaceRecord(5, 7, 8, 0);  //this is one based right? Furthermore, this is a triangle, so the last argument is 0. I hope my intepretation is correct!
                    polyMesh.AppendFaceRecord(fr4);
                    ts.AddNewlyCreatedDBObject(fr4, true);

                    platformSurface.DrawingObjectsDefinition.AddFromPolyFaces(new ObjectIdCollection(new[] { polyMesh.Id }),
                        true, $"Normal Platform");

                    var spuriousEdges = platformSurface.GetEdges().Where(ee => !ee.IsLocked).ToList(); //IsLocked indicates that it's not a part of the edge in PolyMesh

                    if (spuriousEdges.Count > 0)
                    {

                        platformSurface.DeleteLines(spuriousEdges);
                    }

                    platformSurface.Rebuild();
                    ACADEditor.ZoomExtents();
                    ts.Commit();
                }
            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

 Maybe it's how I use the IsLocked filter to delete the spurious edge? But if I don't do it, then my TinSurface will be bigger than the PolyFace mesh! 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

soonhui
Collaborator
Collaborator

How can I tell whether the edge is visible or not? There is no IsVisible property at TinSurfaceEdge class

 

And the way that you suggest seems not robust enough-- what about the the edge(s) on "hole" inside the PolyFaceMesh? 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

Jeff_M
Consultant
Consultant

The FaceRecord has the IsEdgeVisibleAt property. If false then get the midpoint of that edge, use the TinSurface.FindEdgeAtXY() and remove that TinSurfaceEdge. (I haven't used PolyFace Meshes or FaceRecords enough to know how to calculate the FaceRecord edge midpoint.)

 

The IsLocked property is supposed to apply to edges at breaklines, the PolyFaceMesh does not create breaklines so it appears to be a bit random which report as being unlocked or not...hence the unwanted results you are seeing.

Jeff_M, also a frequent Swamper
EESignature
0 Likes

Jeff_M
Consultant
Consultant
I should add that since I don't typically use PolyFaceMesh objects I'm just assuming that the "hole" areas have FaceRecord edges that are not visible. If this is not the case then my idea won't work. It may work in reverse, however, by checking each TinSurfaceEdge to see if there is a corresponding FaceRecord at that location.
Jeff_M, also a frequent Swamper
EESignature
0 Likes

soonhui
Collaborator
Collaborator

@Jeff_M , I' not too sure I understand you. 

 

Yes, I define a few FaceRecords in my PolyFaceMesh. But for the Triangles ( or edges) I want to remove in TinSurface, they have no corresponding FaceRecords in PolyFaceMesh-- when I use TinSurface.DrawingObjects.AddFromPolyFaces to add a PolyFaceMesh the resultant TinSurface will create extra triangles that are not in the PolyFaceMesh. 

 

So how can it be even possible that I check for the IsEdgeVisibleAt property of FaceRecord, when the FaceRecord doesn't even exist?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

Jeff_M
Consultant
Consultant

@soonhui wrote:

So how can it be even possible that I check for the IsEdgeVisibleAt property of FaceRecord, when the FaceRecord doesn't even exist?


This is why I made my addendum post. Use the TinSurfaceEdges to check if there is a corresponding FaceRecordEdge, if not, flag that edge for deletion.

Jeff_M, also a frequent Swamper
EESignature
0 Likes

soonhui
Collaborator
Collaborator

Use the TinSurfaceEdges to check if there is a corresponding FaceRecordEdge

 

It's not that simple because The TinSurface might break the FaceRecordEdge and introduce extra point when two FaceRecordEdge intersect ( refer to this example; One of the edge along the face fr2 will be broken into 2 and a new points introduced). So I have to introduce some numerical tolerance when I check for the line intersection... which is very messy. 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

soonhui
Collaborator
Collaborator

Now I've solved for the last two test cases , but the very original test case is still unsolved.

 

The reason why the last two test cases have missing area is because of how I construct the PolyFaceMesh. It seems that for FaceRecord in PolyFaceMesh, you will have to break it out and make it into a few FaceRecords, if there is an intersecting point on the Edge of the FaceRecord. So for the L shape PolyFaceMesh, the way to fix it is to generate extra FaceRecord, as shown below:

 

facerecrod.png

Note that the intersecting point at the middle should be used as a basis to form extra FaceRecords.

 

But still, the first test case doesn't seem to have this problem ( ie. all the necessary intersecting points are created and used to create FaceRecords), so I'm unsure why there are still missing areas. 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

soonhui
Collaborator
Collaborator

Based on the communication with Civil 3D developer, I can confirm that the problem in the original test case is a bug

 

A fix is underway, but don't know when will come. 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

soonhui
Collaborator
Collaborator

Using the drawing (polymeshback.dwg) in this original message and the code, I still find that the missing area is there, even in the Civil 3D 2024.2. My version:

 

c3d version.png

 

I've highlighted the missing area as per below

Image_7.png

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

WebberHu
Autodesk
Autodesk

Hi @soonhui ,

 

The result looks weird, I had different result on my machine.

Let me double check if we are using the same way to create surface:

  1. How did you create the surface from polymesh: from API or UI?
  2. Did you check "Maintain edges from objects" when you added the polymesh?
  3. Did you set "Exclude out of range triangles when adding polymesh" to YES?
0 Likes

soonhui
Collaborator
Collaborator

@WebberHu , thank you for responding.

 

I'm unclear what do you mean by "if we are using the same way to create surface". You can just run my code in the original message to create the surface right? How can we be different in that way?

 

Or do you mean the surface in the "polymeshback.dwg" file?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here

0 Likes

WebberHu
Autodesk
Autodesk

@soonhui Yes, I see the different surface in the "polymeshback.dwg" file. I am using C3D 2025 alpha.

0 Likes

soonhui
Collaborator
Collaborator
Accepted solution

Hmm, I've done further testing, it does seem that the problem is solved in Civil 3D 2024.2 version.

 

Thank you for your effort. 

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software.

Read more at here