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:
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?
Solved! Go to Solution.
Solved by soonhui. Go to Solution.
Here's another (similar) drawing, and the problem looks even worse:
I'm unsure whether this is a bug, or just my code of converting from PolyFaceMesh to TinSurface is not robust.
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.
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.
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!
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?
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 , 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?
@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.
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.
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:
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.
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.
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:
I've highlighted the missing area as per below
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:
@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?
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.