System.ArgumentException: 'Value does not fall within the expected range.' when AddFromPolyFaces

System.ArgumentException: 'Value does not fall within the expected range.' when AddFromPolyFaces

soonhui
Advisor Advisor
897 Views
10 Replies
Message 1 of 11

System.ArgumentException: 'Value does not fall within the expected range.' when AddFromPolyFaces

soonhui
Advisor
Advisor

I'm trying to add a mesh ( well, in this case, it's just a triangle) to Surface, via the following code:

 

 

 

 

 

    
 private Editor ACADEditor => ActiveACADDocument.Editor;

        private static Document ActiveACADDocument => ACADDocumentManager.MdiActiveDocument;

        private static DocumentCollection ACADDocumentManager => Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager;

        private CivilDocument ActiveCivil3DDocument=> CivilApplication.ActiveDocument;
 
       [CommandMethod("FromMeshTest")]
        public void FromMesh()
        {
            try
            {
                using (var ts = ActiveACADDocument.TransactionManager.StartOpenCloseTransaction())
                {
                    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 polyMesh = new PolyFaceMesh();
                    polyMesh.SetDatabaseDefaults();
                    btr.AppendEntity(polyMesh);
                    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 faceRecord = 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(faceRecord);
                    ts.AddNewlyCreatedDBObject(faceRecord, true);
                    platformSurface.DrawingObjectsDefinition.AddFromPolyFaces(new ObjectIdCollection(new[] { polyMesh.Id }),
                        true, $"Normal Platform");
                    platformSurface.Rebuild();
                    ts.Commit();
                    ACADEditor.ZoomExtents();
                }
            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.ToString());
            }
      
        }

 

 

 

 

 

But, running the above code will throw me an exception at:

 

 

 

 

 

  Autodesk.Civil.DatabaseServices.SurfaceDefinitionDrawingObjects.AddFromPolyFaces(Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection, bool, string)
 
	
System.ArgumentException: 'Value does not fall within the expected range.'

 

 

 

 

 

This is strange because I thought that I am passing in the correct arguments; as my ObjectIdCollection is a list of PolyFaceMesh, the kind of object types that the guide approves!

 

Anything that I miss?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Accepted solutions (1)
898 Views
10 Replies
Replies (10)
Message 2 of 11

Jeff_M
Consultant
Consultant

I don't know what that method is failing, I was unable to get it to work as well. However, going with 3Dfaces instead of a PolyMesh this does work.

        [CommandMethod("FromMeshTest2")]
        public void FromMesh2()
        {
            try
            {
                var faces = new ObjectIdCollection();
                using (var ts = ActiveACADDocument.TransactionManager.StartOpenCloseTransaction())
                {
                    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 vertex1 = new Point3d(0, 0, 0);
                    var vertex2 = new Point3d(10, 0, 0);
                    var vertex3 = new Point3d(10, 10, 0);
                    var polyMesh = new Face(vertex1,vertex2,vertex3,true,true,true,true);
                    polyMesh.SetDatabaseDefaults();
                    btr.AppendEntity(polyMesh);
                    ts.AddNewlyCreatedDBObject(polyMesh, true);
                    faces.Add(polyMesh.ObjectId);
                    platformSurface.DrawingObjectsDefinition.AddFrom3DFaces(faces, true, $"Normal Platform");
                    platformSurface.Rebuild();

                    //ACADEditor.ZoomExtents();
                    ts.Commit();
                }

            }
            catch (System.Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.ToString());
            }

        }
Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 3 of 11

soonhui
Advisor
Advisor

@Jeff_M , I do think that the exception is a bug. Would you want to file a case on this on the internal Autodesk bug tracker? If not I would want to find a way to do it.

 

Secondly, I can also get AddFrom3DFaces to work. But my concern now is that since I already know that my data is in the form of PolyFaceMeshVertex, using Face ( and hence the AddFrom3DFaces ) to store/process it is quite a wastage because it would store/process a lot of duplicated Edges and Points.

 

Is my understanding correct?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Message 4 of 11

Jeff_M
Consultant
Consultant

@TimYarris is there anyone still working on updating the Civil 3D .NET API? If so, could you ask them to provide a working example of how this method works? If they end up with the same issue as posted here, perhaps they could get it corrected? 

 

Thanks for looking ta this Tim.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 5 of 11

soonhui
Advisor
Advisor

Thank you @Jeff_M and @TimYarris .

 

Let me describe my use case to illuminate the importance of this post.

 

I have mesh(es) as my surface(s). Mesh as in the sense of a list of points and triangles, and the triangles can be disjointed ( ie: visually you can see two separated "meshes"  in this one big Mesh data structure), or it can contain holes ( you can imagine it as a mesh where some internal triangles and edges and points are being deleted). 

 

So as you can see, AddFromPolyFaces is the most suitable method, as it directly maps to my data structure (PolyFaceMesh is used exactly like how I would use my Mesh). With the condition that the method works as we think it should ( ie: edges and triangles and points are really maintained)

 

I do hope that this issue is resolved, because I've explored some alternatives like using AddFrom3DFaces and I do think that it's not suitable.

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Message 6 of 11

Tristone
Autodesk
Autodesk

@soonhui I modified your sample codes and it should run successfully.

        [CommandMethod("FromMeshTestFixed")]
        public void FromMeshTestFixed()
        {
            try
            {
                ObjectId polyMeshId;
                using (var ts = ActiveACADDocument.TransactionManager.StartOpenCloseTransaction())
                {
                    
                    

                    var bt = (BlockTable)ts.GetObject(ActiveACADDocument.Database.BlockTableId, OpenMode.ForRead, false);
                    var btr = (BlockTableRecord)ts.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false);
                    var polyMesh = new PolyFaceMesh();
                    polyMesh.SetDatabaseDefaults();
                    btr.AppendEntity(polyMesh);
                    ts.AddNewlyCreatedDBObject(polyMesh, true);
                    polyMeshId = polyMesh.Id;
                    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 faceRecord = 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(faceRecord);
                    ts.AddNewlyCreatedDBObject(faceRecord, true);
                    
                    ts.Commit();
                }

                using (var ts2 = ActiveACADDocument.TransactionManager.StartOpenCloseTransaction())
                {
                    var surfaceId2 = TinSurface.Create(ActiveACADDocument.Database, "FromMeshTest");
                    var platformSurface = ts2.GetObject(surfaceId2, OpenMode.ForWrite) as TINSurface;
                    platformSurface.DrawingObjectsDefinition.AddFromPolyFaces(new ObjectIdCollection(new[] { polyMeshId }),
                       true, $"Normal Platform");
                    platformSurface.Rebuild();
                    ts2.Commit();
                }
            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.Message);
            }

        }
    }

 

My primary change is to move the creation of PolyMeshFace object and surface operations into two different transactions.  We often meet problems when using a new created object before bounded transaction is committed.
I strongly suggest you could try this way to solve similar issues.
The other minor change is
Add ts.AddNewlyCreatedDBObject(polyMesh, true); so that the mesh object could be added successfully. 

                    polyMesh.SetDatabaseDefaults();
                    btr.AppendEntity(polyMesh);
                    ts.AddNewlyCreatedDBObject(polyMesh, true);



 

 



Tristone Hua
Software Development Manager
AEC-BID-Infrastructure
Autodesk, Inc.

Message 7 of 11

Tristone
Autodesk
Autodesk

BTW, it's better to use e.Message instead of e.ToString() to get detail description from Exception. 
"'Value does not fall within the expected range.'" is the general description for ArgumentException from Microsoft. 



Tristone Hua
Software Development Manager
AEC-BID-Infrastructure
Autodesk, Inc.

Message 8 of 11

soonhui
Advisor
Advisor

@Tristone , thanks, your solution works.

 

BUT,  

 


 We often meet problems when using a new created object before bounded transaction is committed.

 

Is this a bug?  Because "often" here means that sometimes it's OK, but sometimes it's problematic. Shouldn't everything be made consistent: either you definitely can use a newly created object before the transaction is committed, or you definitely can't?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Message 9 of 11

Jeff_M
Consultant
Consultant

@Tristone Thanks for this. What I find odd is that I tested nearly identical code and it was still failing. I didn't save that version of the tests so not sure what you did different. Glad to see it does work.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 10 of 11

Tristone
Autodesk
Autodesk
Accepted solution

That's a good question, @soonhui.
It led me to have a deeper investigation, and I found the root cause is you were using the StartOpenCloseTransation(). If you change it to StartTransaction(), you'll find you don't have to use two transactions at all.

using (var ts = ActiveACADDocument.TransactionManager.StartTransaction())

StartOpenCloseTransation will not start a real transaction, please refer to Difference between: StartOpenCloseTransaction vs StartTransaction.
The direct error we are facing is to open a db object(PolyMeshFace here) before it was created and closed. A real transaction can handle it well, and we suggest AutoCAD API user use TransactionManager.StartTransaction rather than TransactionManager.StartCloseTransaction in most cases. 



Tristone Hua
Software Development Manager
AEC-BID-Infrastructure
Autodesk, Inc.

Message 11 of 11

Tristone
Autodesk
Autodesk
You're welcome. Maybe in your first try, you haven't added the below code line?
ts.AddNewlyCreatedDBObject(polyMesh, true);


Tristone Hua
Software Development Manager
AEC-BID-Infrastructure
Autodesk, Inc.

0 Likes