Getting the number of vertices for a polyline2d object

Getting the number of vertices for a polyline2d object

GeeHaa
Collaborator Collaborator
8,146 Views
9 Replies
Message 1 of 10

Getting the number of vertices for a polyline2d object

GeeHaa
Collaborator
Collaborator

Hi

 

I'm looking to get the number of vertices in a polyline2d object. I see it for a regular polyline.Noofvertices object but not for polyline2d.

 

Thanks in Advance

0 Likes
8,147 Views
9 Replies
Replies (9)
Message 2 of 10

norman.yuan
Mentor
Mentor

Still using Polyline2d (or working on really old drawings)? Polyline (LwPolyline) has been around for so long that dealing with old Polyline2d becomes really rare case.

 

Anyway, Polyline2d is an collection of Vetex2d (IEnumerable<Vertex2d>). Thus, if you have gotten an instance of Polyline2d object, you should be able to tell its count of vertices, like:

 

Mypolyline.Count()

 

or

 

Mypolyline.ToList().Count/ToArray().Length

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 10

_gile
Consultant
Consultant

Hi

 

Extract from GeometryExtensions library >>here<<

 

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using AcRx = Autodesk.AutoCAD.Runtime;

namespace GeometryExtensions
{
    /// <summary>
    /// Provides extension methods for the Polyline2d type.
    /// </summary>
    public static class Polyline2dExtensions
    {
        /// <summary>
        /// Gets the vertices list of the polyline 2d.
        /// </summary>
        /// <param name="pl">The instance to which the method applies.</param>
        /// <returns>The vertices list.</returns>
        /// <exception cref="Autodesk.AutoCAD.Runtime.Exception">
        /// eNoActiveTransactions is thrown if the method is not called form a Transaction.</exception>
        public static List<Vertex2d> GetVertices(this Polyline2d pl)
        {
            Transaction tr = pl.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new AcRx.Exception(AcRx.ErrorStatus.NoActiveTransactions);

            List<Vertex2d> vertices = new List<Vertex2d>();
            foreach (ObjectId id in pl)
            {
                Vertex2d vx = (Vertex2d)tr.GetObject(id, OpenMode.ForRead);
                if (vx.VertexType != Vertex2dType.SplineControlVertex)
                    vertices.Add(vx);
            }
            return vertices;
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 10

GeeHaa
Collaborator
Collaborator

Thanks for the response but intellisense doesn't show anything for either of those. Am I missing something?

0 Likes
Message 5 of 10

norman.yuan
Mentor
Mentor

Correction to my previous reply:

 

While Polyline2d is a collection of Vertx2d object, the actual elements in the IEnumerable collection is ObjectId, just like BlockTableRecord.

 

So, the code to get count quickly would be like

 

var count=MyPolyline.Cast<ObjectId>().Count();

 

Or, if you need to deal with each vertices, you can simply open each vertex2d, as Gille's code shows.

Norman Yuan

Drive CAD With Code

EESignature

Message 6 of 10

_gile
Consultant
Consultant

To just get the number of vetrices, you can do as @norman.yuan shows in its last reply.

 

If you want to discard control vertices of splined polyline 2d:

 

poly2d
.Cast<ObjectId>()
.Select(id => (Vertex2d)tr.GetObject(id, OpenMode.ForRead))
.Where(vx => vx.VertexType != Vertex2dType.SplineControlVertex)
.Count();


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 7 of 10

ActivistInvestor
Mentor
Mentor

@norman.yuan wrote:

Correction to my previous reply:

 

While Polyline2d is a collection of Vertx2d object, the actual elements in the IEnumerable collection is ObjectId, just like BlockTableRecord.

 

So, the code to get count quickly would be like

 

var count=MyPolyline.Cast<ObjectId>().Count();

 

Or, if you need to deal with each vertices, you can simply open each vertex2d, as Gille's code shows.


Actually, that's true if the polyline is Database-resident, but if it isn't (e.g., produced by calling Explode() on a BlockReference), then the objects enumerated by the Polyline2d are not ObjectIds, they are Vertex2d objects.

 

See below for a uniform way to access any Polyline2d's vertices regardless of whether its Database-resident or not.

 

 

 

 

0 Likes
Message 8 of 10

ActivistInvestor
Mentor
Mentor

@GeeHaa wrote:

Hi

 

I'm looking to get the number of vertices in a polyline2d object. I see it for a regular polyline.Noofvertices object but not for polyline2d.

 

Thanks in Advance


While what follows might appear to be a case of swatting flies with a sledge hammer, it may prove useful in the future, depending on your needs.

 

Below are some of the extension methods that I use for dealing with 'complex' entities. Their purpose is to abstract away the complexity that results from their differing behavior, depending on if they are in a database or not. When complex entities are not in a database, they enumerate subentity objects. When they're in a database, they enumerate ObjectIds of subentity objects. This can make writing reusable code difficult because it must discriminate between complex entities that are/are-not database-resident. The extension methods allows those complex entity types to be dealt with uniformly and without regards for whether they are in a database or not.

 

To simply get the count of the vertices of any Polyline, Polyline2d, or Polyline3d, you can invoke the GetVertexCoordinates() extension method and pass the result to the Linq Count() method. The includeControlPoints argument indicates if control points should be included in the result, or not.

 

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace Autodesk.AutoCAD.DatabaseServices
{
   /// <summary>
   /// Extension methods that support uniform ways to access subentities
   /// of objects that expose them, abstracting away some of the complexity 
   /// resulting from differing behavior, depending on if the owner Entity
   /// is datbase-resident or not.
   /// 
   /// All extension methods that accept an Entity that is the owner of
   /// subentities will produce uniform results, regardless of whether the
   /// owner entity is database-resident or not.
   /// </summary>
   public static class SubentityExensions
   {
      /// <summary>
      /// Get the vertex world coordinates of a Polyline, Polyline2d, or Polyline3d
      /// </summary>
      /// <remarks>This method can be used on any Polyline, Polyline2d, or
      /// Polyline3d, including those that are not Database-resident and
      /// enumerate vertex entities rather than ObjectIds.
      /// 
      /// Control vertices can be excluded from the result if the 
      /// includeControlPoints argument is false</remarks>
      /// <param name="polyline">A Polyline, Polyline2d, or Polyline3d</param>
      /// <param name="trans">The transaction to use to open Vertex objects,
      /// which is used only if the owner entity is database-resident.</param>
      /// <param name="includingControlPoints">True to include control point
      /// vertices of Polyline2d or Polyline3d</param>
      /// <returns>An object that enumerates the polyline's vertex coordinates</returns>

      public static IEnumerable<Point3d> GetVertexCoordinates(this Curve polyline, Transaction trans, bool includingControlPoints = false)
      {
         Assert.IsNotNull(polyline, "polyline");
         Polyline pline = polyline as Polyline;
         if(pline != null)
            return Enumerable.Range(0, pline.NumberOfVertices).Select(i => pline.GetPoint3dAt(i));
         Polyline2d pline2d = polyline as Polyline2d;
         if(pline2d != null)
            return pline2d.GetVertices(trans)
               .Where(v => includingControlPoints || v.VertexType != Vertex2dType.SplineControlVertex)
               .Select(v => pline2d.VertexPosition(v));
         Polyline3d pline3d = polyline as Polyline3d;
         if(pline3d != null)
            return pline3d.GetVertices(trans)
               .Where(v => includingControlPoints || v.VertexType != Vertex3dType.ControlVertex)
               .Select(v => v.Position);
         ErrorStatus.WrongObjectType.Check(false);
         return null;
      }

      /// <summary>
      /// Enumerates the vertices of a database-resident or memory-resident Polyine2d.
      /// </summary>
      /// <remarks>This method will enumerate the vertices of any Polyline2d, including
      /// those that are not database-resident. 
      /// 
      /// Its purpose is to abstract away the complexity imposed by the fact that different 
      /// types are enumerated by a Polyline2d, depending on if the polyline is database-
      /// resident or not. 
      /// 
      /// If a Polyline2d is database-resident, it will enumerate the ObjectIds of the 
      /// polyline's database-resident vertices. If a Polyline2d is not database-resident, 
      /// it will enumerate Vertex2d objects that are also not database-resident.</remarks>
      /// <param name="polyline">The Polyline2d whose vertices are to be retrieved</param>
      /// <param name="mode">The OpenMode used for opening database-resident Vertex2d objects</param>
      /// <param name="trans">The Transaction to use to open database-resident Vertex2d objects.
      /// If this argument is not supplied, the method will attempt to use the Top Transaction
      /// of the database containing the polyline, or will raise an exception if there is no
      /// Top transaction.
      /// <returns>An object that enumerates the polyline's vertices</returns>

      public static IEnumerable<Vertex2d> GetVertices(this Polyline2d polyline, OpenMode mode = OpenMode.ForRead, Transaction trans = null)
      {
         Assert.IsNotNull(polyline, "polyline");
         return GetSubEntities<Vertex2d>(polyline, mode, trans);
      }

      public static IEnumerable<Vertex2d> GetVertices(this Polyline2d polyline, Transaction trans = null)
      {
         Assert.IsNotNull(polyline, "polyline");
         return GetSubEntities<Vertex2d>(polyline, OpenMode.ForRead, trans);
      }

      /// <summary>
      /// Same as above, except for use with Polyline3d/PolylineVertex3d
      /// </summary>

      public static IEnumerable<PolylineVertex3d> GetVertices(this Polyline3d polyline, OpenMode mode = OpenMode.ForRead, Transaction trans = null)
      {
         Assert.IsNotNull(polyline, "polyline");
         return GetSubEntities<PolylineVertex3d>(polyline, mode, trans);
      }

      public static IEnumerable<PolylineVertex3d> GetVertices(this Polyline3d polyline, Transaction trans = null)
      {
         Assert.IsNotNull(polyline, "polyline");
         return GetSubEntities<PolylineVertex3d>(polyline, OpenMode.ForRead, trans);
      }

      /// <summary>
      /// Same as above, except for use with PolygonMesh/PolygonMeshVertex
      /// </summary>

      public static IEnumerable<PolygonMeshVertex> GetVertices(this PolygonMesh mesh, OpenMode mode = OpenMode.ForRead, Transaction trans = null)
      {
         Assert.IsNotNull(mesh, "mesh");
         return GetSubEntities<PolygonMeshVertex>(mesh, mode, trans);
      }

      public static IEnumerable<PolygonMeshVertex> GetVertices(this PolygonMesh mesh, Transaction trans = null)
      {
         Assert.IsNotNull(mesh, "mesh");
         return GetSubEntities<PolygonMeshVertex>(mesh, OpenMode.ForRead, trans);
      }

      /// <summary>
      /// Same as above, except for use with PolyFaceMesh/PolyFaceMeshVertex
      /// </summary>

      public static IEnumerable<PolyFaceMeshVertex> GetVertices(this PolyFaceMesh mesh, OpenMode mode = OpenMode.ForRead, Transaction trans = null)
      {
         Assert.IsNotNull(mesh, "mesh");
         return GetSubEntities<PolyFaceMeshVertex>(mesh, mode, trans);
      }

      public static IEnumerable<PolyFaceMeshVertex> GetVertices(this PolyFaceMesh mesh, Transaction trans = null)
      {
         Assert.IsNotNull(mesh, "mesh");
         return GetSubEntities<PolyFaceMeshVertex>(mesh, OpenMode.ForRead, trans);
      }

      /// <summary>
      /// Enumerates the AttributeReference objects owned by a given BlockReference
      /// </summary>
      /// <remarks>This method will uniformly enumerate the AttributeReference objects 
      /// of a BlockReference, including those that are not database-resident. 
      /// 
      /// Its purpose is to abstract away the inherint complexity resulting from the 
      /// fact that different kinds of objects are contained in a BlockReference's 
      /// AttributeCollection, depending on if the BlockReference is database-resident, 
      /// or not. 
      /// 
      /// If a BlockReference is database-resident, its AttributeCollection contains
      /// the ObjectIds of its AttributeReferences. If a BlockReference is not database-
      /// resident, its AttributeCollection contains its AttributeReference objects,
      /// that are also not database-resident.</remarks>
      /// <param name="blockRef">The BlockReference whose attributes are to be obtained</param>
      /// <param name="mode">The OpenMode used for opening database-resident AttributeReferences</param>
      /// <param name="trans">The Transaction to be used to open database-resident 
      /// AttributeReferences. If this argument is not provided, the method will attempt
      /// to use the top transaction in the database containing the BlockReference, and
      /// will throw an exception if there is no top transaction</param>
      /// <returns></returns>

      public static IEnumerable<AttributeReference> GetAttributes(this BlockReference blockRef, OpenMode mode = OpenMode.ForRead, Transaction trans = null, bool forceOpenOnLockedLayer = false)
      {
         Assert.IsNotNull(blockRef, "blockRef");
         return GetSubEntities<AttributeReference>(blockRef, mode, trans, blockRef.AttributeCollection, forceOpenOnLockedLayer);
      }

      public static IEnumerable<AttributeReference> GetAttributes(this BlockReference blockRef, Transaction trans = null)
      {
         Assert.IsNotNull(blockRef, "blockRef");
         return GetSubEntities<AttributeReference>(blockRef, OpenMode.ForRead, trans, blockRef.AttributeCollection);
      }

      /// <summary>
      /// Worker for GetAttributes() and all GetVertices() overloads
      /// </summary>

      public static IEnumerable<T> GetSubEntities<T>(this Entity owner, OpenMode mode = OpenMode.ForRead, Transaction trans = null, IEnumerable source = null, bool forceOpenOnLockedLayer = false) where T : Entity
      {
         Assert.IsNotNull(owner, "owner");
         IEnumerable enumerable = source ?? owner as IEnumerable;
         ErrorStatus.InvalidInput.Check(enumerable != null, "No IEnumerable");
         if(owner.Database != null)
         {
            trans = trans ?? owner.Database.TransactionManager.TopTransaction;
            ErrorStatus.NoActiveTransactions.Check(trans != null);
            foreach(ObjectId id in enumerable)
               yield return (T) trans.GetObject(id, mode, false, forceOpenOnLockedLayer);
         }
         else
         {
            foreach(T entity in enumerable)
               yield return entity;
         }
      }

   }
}

namespace Autodesk.AutoCAD.Runtime
{
   using AcRx = Autodesk.AutoCAD.Runtime;
   public static class RuntimeExtensions
   {
      public static void Check(this ErrorStatus es, bool condition, string msg = null)
      {
         if(!condition)
         {
            if(msg == null)
               throw new AcRx.Exception(es);
            else
               throw new AcRx.Exception(es, msg);
         }
      }
   }
}


namespace System.Diagnostics
{
   public static class Assert
   {
      public static void IsNotNull<T>(T obj, string name = null) where T : class
      {
         if(obj == null)
            throw new ArgumentNullException(name ?? "unspecified");
      }
   }
}

 

 

Message 9 of 10

ActivistInvestor
Mentor
Mentor

Allow me to correct myself, I'm just not in a thinking mood today. 

 

For your purpose, all of that code I posted above probably is a sledge hammer. It was mainly purposed for accessing vertices rather than just their coordinates.

 

If you don't care about control vertices (which is most-often the case), you can use the second extension method below:

 

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace Autodesk.AutoCAD.DatabaseServices
{

   public static class PolylineExtensions
   {
      /// <summary>
      /// Returns the WCS vertex coordinates of any Polyline, Polyline2d, 
      /// or Polyline3d. The result always excludes control point vertices.
      /// </summary>
      
      public static IEnumerable<Point3d> GetVertexPoints(this Curve pline)
      {
         if(pline == null)
            throw new ArgumentNullException("pline");
         if(!((pline is Polyline) || (pline is Polyline2d) || pline is Polyline3d))
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.WrongObjectType);
         if(pline.EndParam % 1.0 > 1.0e-10)
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput);
         int vertices = ((int) pline.EndParam) + (pline.Closed ? 0 : 1);
         for(int i = 0; i < vertices; i++)
            yield return pline.GetPointAtParameter(i);
      }

      /// <summary>
      /// Returns the number of vertices in the given Polyline, Polyline2d, or Polyline3d.
      /// The result always excludes control point vertices.
      /// </summary>
      
      public static int GetVertexCount(this Curve pline)
      {
         if(pline == null)
            throw new ArgumentNullException("pline");
         if(!((pline is Polyline) || (pline is Polyline2d) || pline is Polyline3d))
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.WrongObjectType);
         if(pline.EndParam % 1.0 > 1.0e-10)
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput);
         return ((int) pline.EndParam) + (pline.Closed ? 0 : 1);
      }
   }
}

 

Message 10 of 10

SENL1362
Advisor
Advisor
LOL: swatting files with a sledge hammer
0 Likes