Iterating through a polyline's vertices using AutoCAD .NET

I was inspired by some code sent out by Viru Aithal to write a command that iterates through the vertices of the various types of AutoCAD polyline:

  • Optimized (or "lightweight") 2D polylines, which store an array of 2D vertices
  • Old-format (or "heavyweight") 2D polylines, which contain a sequence of 2D vertex objects
  • 3D polylines, which contain a sequence of 3D vertex objects

Polylines that contain vertex objects are containers for the object IDs for the various vertices - you can use foreach to loop through the vertex objects, opening them one-by-one.

For optimized polylines we need to loop using a counter which gets passed as an index to a retrieval function. In this case we use GetPoint2dAt() to get the Point2d at that index, but we might also have used other access functions, depending on our interest:

  • GetPoint3dAt()
  • GetLineSegmentAt()
  • GetLineSegment2dAt()
  • GetArcSegmentAt()
  • GetArcSegment2dAt()
  • GetBulgeAt()
  • GetStartWidthAt()
  • GetEndWidthAt()

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

namespace IterateObjects

{

  public class Commands

  {

    [CommandMethod("LV")]

    static public void ListVertices()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      PromptEntityResult per =

        ed.GetEntity("Select a polyline");

      if (per.Status == PromptStatus.OK)

      {

        Transaction tr =

          db.TransactionManager.StartTransaction();

        using (tr)

        {

          DBObject obj =

            tr.GetObject(per.ObjectId, OpenMode.ForRead);

          // If a "lightweight" (or optimized) polyline

          Polyline lwp = obj as Polyline;

          if (lwp != null)

          {

            // Use a for loop to get each vertex, one by one

            int vn = lwp.NumberOfVertices;

            for (int i = 0; i < vn; i++)

            {

              // Could also get the 3D point here

              Point2d pt = lwp.GetPoint2dAt(i);

              ed.WriteMessage("\n" + pt.ToString());

            }

          }

          else

          {

            // If an old-style, 2D polyline

            Polyline2d p2d = obj as Polyline2d;

            if (p2d != null)

            {

              // Use foreach to get each contained vertex

              foreach (ObjectId vId in p2d)

              {

                Vertex2d v2d =

                  (Vertex2d)tr.GetObject(

                    vId,

                    OpenMode.ForRead

                  );

                ed.WriteMessage(

                  "\n" + v2d.Position.ToString()

                );

              }

            }

            else

            {

              // If an old-style, 3D polyline

              Polyline3d p3d = obj as Polyline3d;

              if (p3d != null)

              {

                // Use foreach to get each contained vertex

                foreach (ObjectId vId in p3d)

                {

                  PolylineVertex3d v3d =

                    (PolylineVertex3d)tr.GetObject(

                      vId,

                      OpenMode.ForRead

                    );

                  ed.WriteMessage(

                    "\n" + v3d.Position.ToString()

                  );

                }

              }

            }

          }

          // Committing is cheaper than aborting

          tr.Commit();

        }

      }

    }

  }

}

3 responses to “Iterating through a polyline's vertices using AutoCAD .NET”

  1. While transaction.Commit() may be faster than Dispose() (I wasn't aware of this), it's still not a good idea. If I remember correctly, this sets the documents modified flag which will cause a save prompt upon closing even though you didn't change anything.

  2. There's a DevNote on the ADN site covering this topic:

    adn.autodesk.com/adn...

    Here's an excerpt:

    >>>
    ... there is a huge performance difference between Commit() and Abort(). So, when opening objects for read, you definitely want to explicitly call Commit() even though you didn't really change anything. As evidenced by a few test functions, Abort() should only be used under "duress" because of the performance hit.
    <<<

    A read-only transaction (even one that opens objects for write, but doesn't modify them) will not set the database modified flag (DBMOD) when committed. It's the individual "set" methods that call assertWriteEnabled(), forcing DBMOD to non-zero.

    1. There's a DevNote on the ADN site covering this topic:

      adn.autodesk.com/adn...

      Here's an excerpt:

      Hi Kean.
      Is this DevNote available for public access ??

      Regards,

      1. Honestly I have no idea. I did try to search for it - even on the ADN site - but couldn't find it. I have a feeling that most (all?) DevNotes got migrated to blog posts, but I wasn't involved in that effort myself.

        Kean

  3. Senthil Prabu B R Avatar
    Senthil Prabu B R

    Hi Kean,

    i am trying to set the Global width of lwpolyline with the use of SetStartWidthAt()SetEndWidthAt(). But it is not giving the expected result, Please give me some examples.

  4. You'll need to iterate through each of the vertices - either that or use lineweights.

    Kean

  5. Hi Kean,

    I would need the above code in VB to incorporate it in my VB.NET project. Could you perhaps provide it?
    Actually, I need to iterate through vertices in 3D polyline and extract coordinates.
    Thanks,

  6. Sorry - I don't have time to do that for people.

    This post should help, though.

    Kean

  7. Hi,
    is there an elegant way of getting number of vertices for 3dpolyline? (like Polyline.NumberOfVertices for optimized polyline) Or do i have to iterate through all vertices and count them?

    1. You can use polyline.EndParam + 1
      The parameter of the first vertex is 0.0
      The parameter of the second vertex is 1.0
      .....
      (convert to int)

  8. Kean Walmsley Avatar

    There's no direct way. This StackOverflow thread has some interesting background and options.

    Kean

  9. I don't get something basic, maybe you can help.
    If you define a single line in a polyline,
    how can you tell difference if you can use both "GetArcSegmentAt" and "GetLineSegmentAt"?

  10. Kean Walmsley Avatar

    It's all in the Bulge (unless I've misunderstood the question).

    Kean

  11. got it
    thanks 🙂

  12. Javaknight Technologies Avatar
    Javaknight Technologies

    Great work here Kean. Was pulling my hair out attempting to understand the above Get...At() functions. The crappy intellisense documentation (and other Autodesk documentation) was NOT helping. I knew that these were the functions to employ. I am actually trying to create a selection set that has a crossing window defined by the verticies of a polyline, to retrieve the located text inside of a large number of polylines. This takes me almost to completion.

    Thanks again

    -jk

  13. Kean,

    I'm trying to import DWG drawing data into a standalone application. When I use the foreach construct on a Polyline2d :

    // If an old-style, 2D polyline
    Polyline2d p2d = obj as Polyline2d;
    if (p2d != null)
    {
    // Use foreach to get each contained vertex
    foreach (ObjectId vId in p2d)
    {
    Vertex2d v2d = (Vertex2d)tr.GetObject(vId, OpenMode.ForRead);
    }

    It works for some Polyline2d objects. For others, I get an System.InvalidCastException. For those polyline objects, if I instead use

    foreach (Object obj in p2d)

    I get Vertex2d objects.

    So which type is Polyline2d supposed to iterate through? And how can I tell what to expect?

    Thanks,
    Robert

  14. Robert,

    The enumerator is generic - in that it returns objects of type System.Object - but my understanding is that these should in fact be ObjectIds of Vertex2d objects (for Polyline2d objects) or ObjectIds of Vertex3d objects (for Polyline3ds).

    But then I don't spend much time iterating over old-style polylines, admittedly.

    Have you just tried dumping the object type, to see where/when they are of an unexpected type? You needn't perform a static cast to a Vertex2d object, for instance - just open it as a DBObject and get the class name. I'd be interested to see under which circumstances you get non-Vertex2d objects.

    Regards,

    Kean

  15. Does this function works for AutoCAD 2016?

    I import the .dll into AutoCAD and works fine until I select a Polyline. It arises an exception:

    FATAL ERROR: Unhandled Access Violation Reading 0x0040 Exception at e50f76b0h

    What kind of fixes should I do?

    1. It should, although I haven't tried it myself and I'm just heading out on 2 weeks vacation.

      In the meantime, please post your question to the discussion groups.

      Thanks,

      Kean

  16. The Polyline2D will return a collection of Vertex2D objects directly when a point is being stretched, not ObjectIDs

    1. Does this relate to the above code in some way? Sorry - it's a very old post (and I'm working on other things), so it would help me to have a bit more information.

      Thanks,

      Kean

      1. Sorry should have replied to Robert's Post. Your post is great to get started with Polyline2D objects. Just wanted to add a little info for anyone that might may need it.

        Also in VB the ObjectId.GetObject() has to be called within an open AutoCAD .NET Transaction otherwise get exception 'Object reference not set to an instance of an object’
        Workaround is to use DirectCast(vId.Open(OpenMode.ForRead), Vertex2d)
        (except when dragging of course).
        If this code happens to be in a DrawableOverrule one can check for dragging with wd.isdragging

        1. OK, got it - thanks!

          Kean

  17. hallo, please how cann i do to "set" Id of a Polyline3d

    1. You can't: IDs and handles are assigned by AutoCAD. And as a complex object, a Polyline3d has a number of owned vertices, so it doesn't just have one ID.

      Kean

  18. hi. I just want to know, how to extract the polyline 2d points if there is a extrusion direction. because when use vector2d it return a different coordinates. thank you

    1. Hi. Kean.

      I just found a solution from this link.

      forums.autodesk.com...

      Thanks again.

      Marlon

Leave a Reply to ai Cancel reply

Your email address will not be published. Required fields are marked *