Community
Civil 3D Customization
Welcome to Autodesk’s AutoCAD Civil 3D Forums. Share your knowledge, ask questions, and explore popular AutoCAD Civil 3D Customization topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

16 REPLIES 16
SOLVED
Reply
Message 1 of 17
soonhui
743 Views, 16 Replies

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

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?

16 REPLIES 16
Message 2 of 17
soonhui
in reply to: soonhui

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.

Message 3 of 17
Jeff_M
in reply to: soonhui

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
Message 4 of 17
soonhui
in reply to: soonhui

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! 

Message 5 of 17
soonhui
in reply to: Jeff_M

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? 

Message 6 of 17
Jeff_M
in reply to: soonhui

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
Message 7 of 17
Jeff_M
in reply to: Jeff_M

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
Message 8 of 17
soonhui
in reply to: Jeff_M

@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?

Message 9 of 17
Jeff_M
in reply to: soonhui


@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
Message 10 of 17
soonhui
in reply to: Jeff_M

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. 

Message 11 of 17
soonhui
in reply to: soonhui

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. 

Message 12 of 17
soonhui
in reply to: soonhui

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. 

Message 13 of 17
soonhui
in reply to: soonhui

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

Message 14 of 17
WebberHu
in reply to: soonhui

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?
Message 15 of 17
soonhui
in reply to: WebberHu

@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?

Message 16 of 17
WebberHu
in reply to: soonhui

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

Message 17 of 17
soonhui
in reply to: WebberHu

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. 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


Autodesk Design & Make Report