Author Topic: .NET POLYLINE Routines  (Read 19786 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
.NET POLYLINE Routines
« on: January 27, 2010, 03:11:04 AM »
LIBRARY THREAD for  AutoCAD POLYLINES
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD POLYLINES in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Bryco

  • Water Moccasin
  • Posts: 1882
Re: .NET POLYLINE Routines
« Reply #1 on: January 31, 2010, 12:30:56 PM »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET POLYLINE Routines
« Reply #2 on: January 31, 2010, 12:48:27 PM »
Speaking English as a French Frog

fixo

  • Guest
Re: .NET POLYLINE Routines
« Reply #3 on: May 05, 2010, 07:21:48 AM »
Quick way to export polyline coordinates into .CSV file
Code: [Select]

using System;
using System.Text;
using System.IO;
using System.Collections;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;
---------------------------------------
 [CommandMethod("exps")]
        static public void ExportCoordinates()
        {

            Document doc = acadApp.DocumentManager.MdiActiveDocument;

            Database db = HostApplicationServices.WorkingDatabase;

            Editor ed = doc.Editor;

            string path = @"C:\PolyCoords.csv";

            string carret  = Environment.NewLine;
            //set text delimiter
            string sep = ",";

            StringBuilder sb   = new StringBuilder();
            // set counter
            int cnt = 0;

            PromptEntityOptions opt = new PromptEntityOptions(

                "\nSelect a polyline object: ");

            opt.SetRejectMessage("\nObject must be LWPolyline, Polyline2d or Polyline3d only.");

            opt.AddAllowedClass(typeof(Polyline), false);

            opt.AddAllowedClass(typeof(Polyline2d), false);

            opt.AddAllowedClass(typeof(Polyline3d), false);

            PromptEntityResult res = ed.GetEntity(opt);

            if (res.Status != PromptStatus.OK)

                return;
            //set precision
            int prec = 3;

            PromptIntegerOptions pio = new PromptIntegerOptions(

                "\nEnter number of decimals <3>: " );

            pio.AllowNone = true;

            PromptIntegerResult pir =ed.GetInteger(pio);

            if (pir.Status != PromptStatus.None &&

                pir.Status != PromptStatus.OK)

                return;

            if (pir.Status == PromptStatus.OK)

                prec = pir.Value;

            Transaction tr = db.TransactionManager.StartTransaction();

            using (tr)
            {
                DBObject obj = tr.GetObject(res.ObjectId, OpenMode.ForRead);


                ed.WriteMessage("\n >>>   {0}", obj.GetRXClass().Name);

                if (obj == null)

                    return;

                sb.Append("Point Number,X,Y,Z" + carret);

                switch (obj.GetRXClass().Name)
                {

                    case "AcDbPolyline":
                        {

                            Polyline lwpoly = obj as Polyline;

                            if (lwpoly != null)
                            {
                                for (int i = 0; i < lwpoly.NumberOfVertices; i++)
                                {
                                    cnt += 1;

                                    Point3d pt = lwpoly.GetPoint3dAt(i);

                                    ed.WriteMessage("\nX = {0}; Y = {1}; Z = {2}", pt.X, pt.Y, pt.Z);

                                    sb.Append(cnt.ToString() + sep +

                                       Math.Round(pt.X, prec).ToString() + sep +

                                       Math.Round(pt.Y, prec).ToString() + sep +

                                       Math.Round(pt.Z, prec).ToString() + carret);

                                }
                            }
                            break;
                        }

                    case "AcDb2dPolyline":
                        {

                            Polyline2d poly2d = obj as Polyline2d;

                            if (poly2d != null)
                            {
                                foreach (ObjectId ix in poly2d)
                                {
                                    cnt += 1;

                                    Vertex2d vex = (Vertex2d)ix.GetObject(OpenMode.ForRead);
                                    Point3d pt = vex.Position;

                                    ed.WriteMessage("\nX = {0}; Y = {1}; Z = {2}", pt.X, pt.Y, pt.Z);

                                    sb.Append(cnt.ToString() + sep +

                                       Math.Round(pt.X, prec).ToString() + sep +

                                       Math.Round(pt.Y, prec).ToString() + sep +

                                       Math.Round(pt.Z, prec).ToString() + carret);                               
                                }

                            }
                            break;
                        }

                    case "AcDb3dPolyline":
                        {
                            Polyline3d poly3d = obj as Polyline3d;

                            if (poly3d != null)
                            {                             
                                foreach (ObjectId ix in poly3d)
                                {
                                    cnt += 1;

                                    PolylineVertex3d vex = (PolylineVertex3d)ix.GetObject(OpenMode.ForRead);

                                    Point3d pt = vex.Position;

                                    ed.WriteMessage("\nX = {0}; Y = {1}; Z = {2}", pt.X, pt.Y, pt.Z);

                                    sb.Append(cnt.ToString() + sep +

                                       Math.Round(pt.X, prec).ToString() + sep +

                                       Math.Round(pt.Y, prec).ToString() + sep +

                                       Math.Round(pt.Z, prec).ToString() + carret);

                                }
                               
                            }
                            break;
                        }

                    default:

                        break;
                }

        System.IO.StreamWriter sw   = new StreamWriter(path);

        using (sw)
        {
            sw.Write(sb.ToString());
        }

                tr.Commit();
            }

        }

~'J'~

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET POLYLINE Routines
« Reply #4 on: March 17, 2013, 05:35:40 AM »
Hi,

Defining the Polyline offset side is a recurent issue (left/right or inside/outside).
The polyline.Offset() extension method defined in the PolylineExtension class requires an argument to specify the offset side. This argument is a member of the PolylineExtension.OffsetSide enum.

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5.  
  6. namespace OffsetPolylineSample
  7. {
  8.     /// <summary>
  9.     /// Provides the Offset() extension method for the Polyline type
  10.     /// </summary>
  11.     public static class PolylineExtension
  12.     {
  13.         /// <summary>
  14.         /// Enumeration of offset side options
  15.         /// </summary>
  16.         public enum OffsetSide { In, Out, Left, Right, Both }
  17.  
  18.         /// <summary>
  19.         /// Offset the source polyline to specified side(s).
  20.         /// </summary>
  21.         /// <param name="source">The polyline to be offseted.</param>
  22.         /// <param name="offsetDist">The offset distance.</param>
  23.         /// <param name="side">The offset side(s).</param>
  24.         /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
  25.         public static IEnumerable<Polyline> Offset(this Polyline source, double offsetDist, OffsetSide side)
  26.         {
  27.             offsetDist = Math.Abs(offsetDist);
  28.             IEnumerable<Polyline> offsetRight = source.GetOffsetCurves(offsetDist).Cast<Polyline>();
  29.             double areaRight = offsetRight.Select(pline => pline.Area).Sum();
  30.             IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves(-offsetDist).Cast<Polyline>();
  31.             double areaLeft = offsetLeft.Select(pline => pline.Area).Sum();
  32.             switch (side)
  33.             {
  34.                 case OffsetSide.In:
  35.                     if (areaRight < areaLeft)
  36.                     {
  37.                         offsetLeft.Dispose();
  38.                         return offsetRight;
  39.                     }
  40.                     else
  41.                     {
  42.                         offsetRight.Dispose();
  43.                         return offsetLeft;
  44.                     }
  45.                 case OffsetSide.Out:
  46.                     if (areaRight < areaLeft)
  47.                     {
  48.                         offsetRight.Dispose();
  49.                         return offsetLeft;
  50.                     }
  51.                     else
  52.                     {
  53.                         offsetLeft.Dispose();
  54.                         return offsetRight;
  55.                     }
  56.                 case OffsetSide.Left:
  57.                     offsetRight.Dispose();
  58.                     return offsetLeft;
  59.                 case OffsetSide.Right:
  60.                     offsetLeft.Dispose();
  61.                     return offsetRight;
  62.                 case OffsetSide.Both:
  63.                     return offsetRight.Concat(offsetLeft);
  64.                 default:
  65.                     return null;
  66.             }
  67.         }
  68.  
  69.         private static void Dispose(this IEnumerable<Polyline> plines)
  70.         {
  71.             foreach (Polyline pline in plines)
  72.             {
  73.                 pline.Dispose();
  74.             }
  75.         }
  76.     }
  77. }
  78.  

A testing command
Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5.  
  6. namespace OffsetPolylineSample
  7. {
  8.     public class CommandMethods
  9.     {
  10.         [CommandMethod("Test", CommandFlags.Modal)]
  11.         public void Test()
  12.         {
  13.             Document doc = Application.DocumentManager.MdiActiveDocument;
  14.             Database db = doc.Database;
  15.             Editor ed = doc.Editor;
  16.  
  17.             PromptDistanceOptions pdo =
  18.                 new PromptDistanceOptions("\nSpecify the offset distance: ");
  19.             pdo.AllowZero = false;
  20.             PromptDoubleResult pdr = ed.GetDistance(pdo);
  21.             if (pdr.Status != PromptStatus.OK) return;
  22.             double offsetDist = pdr.Value;
  23.  
  24.             PromptKeywordOptions pko =
  25.                 new PromptKeywordOptions("\nEnter the offset side [In/Out/Left/Right/Both]", "In Out Left Right Both");
  26.             PromptResult pr = ed.GetKeywords(pko);
  27.             if (pr.Status != PromptStatus.OK) return;
  28.             PolylineExtension.OffsetSide side;
  29.             switch (pr.StringResult)
  30.             {
  31.                 case "In": side = PolylineExtension.OffsetSide.In; break;
  32.                 case "Out": side = PolylineExtension.OffsetSide.Out; break;
  33.                 case "Left": side = PolylineExtension.OffsetSide.Left; break;
  34.                 case "Right": side = PolylineExtension.OffsetSide.Right; break;
  35.                 default: side = PolylineExtension.OffsetSide.Both; break;
  36.             }
  37.  
  38.             PromptEntityOptions peo = new PromptEntityOptions("\nSelect a polyline: ");
  39.             peo.SetRejectMessage("Only a polyline !");
  40.             peo.AddAllowedClass(typeof(Polyline), true);
  41.  
  42.             using (Transaction tr = db.TransactionManager.StartTransaction())
  43.             {
  44.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  45.                 while (true)
  46.                 {
  47.                     PromptEntityResult per = ed.GetEntity(peo);
  48.                     if (per.Status != PromptStatus.OK) break;
  49.  
  50.                     Polyline pline = (Polyline)tr.GetObject(per.ObjectId, OpenMode.ForRead);
  51.                     foreach (Polyline pl in pline.Offset(offsetDist, side))
  52.                     {
  53.                         btr.AppendEntity(pl);
  54.                         tr.AddNewlyCreatedDBObject(pl, true);
  55.                     }
  56.                     db.TransactionManager.QueueForGraphicsFlush();
  57.                 }
  58.                 tr.Commit();
  59.             }
  60.         }
  61.     }
  62. }
  63.  
« Last Edit: March 17, 2013, 05:42:34 AM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: .NET POLYLINE Routines
« Reply #5 on: March 17, 2013, 02:07:44 PM »
Hi Gile.

Very nice code.

There is one thing I might be careful of, which is that if one of the methods you call throws an exception, the new Polylines would not be disposed, and that could very likely crash AutoCAD.

Here's one way to deal with it, that ensures that all new Polyline objects are disposed even if the code fails for some reason:

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5.  
  6. namespace OffsetPolylineSample
  7. {
  8.    /// <summary>
  9.    /// Provides the Offset() extension method for the Polyline type
  10.    /// </summary>
  11.    
  12.    public static class PolylineExtension
  13.    {
  14.       /// <summary>
  15.       /// Enumeration of offset side options
  16.       /// </summary>
  17.       public enum OffsetSide
  18.       {
  19.          In, Out, Left, Right, Both
  20.       }
  21.  
  22.       /// <summary>
  23.       /// Offset the source polyline to specified side(s).
  24.       /// </summary>
  25.       /// <param name="source">The polyline to be offseted.</param>
  26.       /// <param name="offsetDist">The offset distance.</param>
  27.       /// <param name="side">The offset side(s).</param>
  28.       /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
  29.       public static IEnumerable<Polyline> Offset( this Polyline source, double offsetDist, OffsetSide side )
  30.       {
  31.          offsetDist = Math.Abs( offsetDist );
  32.          using( var plines = new DisposableSet<Polyline>() )
  33.          {
  34.             IEnumerable<Polyline> offsetRight = source.GetOffsetCurves( offsetDist ).Cast<Polyline>();
  35.             plines.AddRange( offsetRight );
  36.             IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves( -offsetDist ).Cast<Polyline>();
  37.             plines.AddRange( offsetLeft );
  38.             double areaRight = offsetRight.Select( pline => pline.Area ).Sum();
  39.             double areaLeft = offsetLeft.Select( pline => pline.Area ).Sum();
  40.             switch( side )
  41.             {
  42.                case OffsetSide.In:
  43.                   if( areaRight < areaLeft )
  44.                   {
  45.                      plines.RemoveRange( offsetRight );
  46.                      return offsetRight;
  47.                   }
  48.                   else
  49.                   {
  50.                      plines.RemoveRange( offsetLeft );
  51.                      return offsetLeft;
  52.                   }
  53.                case OffsetSide.Out:
  54.                   if( areaRight < areaLeft )
  55.                   {
  56.                      plines.RemoveRange( offsetLeft );
  57.                      return offsetLeft;
  58.                   }
  59.                   else
  60.                   {
  61.                      plines.RemoveRange( offsetRight );
  62.                      return offsetRight;
  63.                   }
  64.                case OffsetSide.Left:
  65.                   plines.RemoveRange( offsetLeft );
  66.                   return offsetLeft;
  67.                case OffsetSide.Right:
  68.                   plines.RemoveRange( offsetRight );
  69.                   return offsetRight;
  70.                case OffsetSide.Both:
  71.                   plines.Clear();
  72.                   return offsetRight.Concat( offsetLeft );
  73.                default:
  74.                   return null;
  75.             }
  76.          }
  77.       }
  78.    }
  79.  
  80.    public class DisposableSet<T> : HashSet<T>, IDisposable where T: IDisposable
  81.    {
  82.       public void Dispose()
  83.       {
  84.          System.Exception last = null;
  85.          foreach( T item in this )
  86.          {
  87.             if( item != null )
  88.             {
  89.                try
  90.                {
  91.                   item.Dispose();
  92.                }
  93.                catch( System.Exception ex )
  94.                {
  95.                   last = last ?? ex;
  96.                }
  97.             }
  98.          }
  99.          this.Clear();
  100.          if( last != null )
  101.             throw last;
  102.       }
  103.  
  104.       public void AddRange( IEnumerable<T> items )
  105.       {
  106.          foreach( T item in items )
  107.          {
  108.             if( item == null )
  109.                throw new ArgumentNullException( "element" );
  110.             base.Add( item );
  111.          }
  112.       }
  113.  
  114.       public void RemoveRange( IEnumerable<T> items )
  115.       {
  116.          foreach( T item in items )
  117.          {
  118.             if( item != null )
  119.                base.Remove( item );
  120.          }
  121.       }
  122.    }
  123.  
  124. }
  125.  

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET POLYLINE Routines
« Reply #6 on: March 17, 2013, 04:32:32 PM »
Thank you Tony.

That makes sense. I'm still learning from your tricks.
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET POLYLINE Routines
« Reply #7 on: March 18, 2013, 06:52:58 PM »
Tony,

What about using the HashSet built-in UnionWith() and ExceptWith() method instead of defining the AddRange() and RemoveRange() ones ?
Speaking English as a French Frog

TheMaster

  • Guest
Re: .NET POLYLINE Routines
« Reply #8 on: March 18, 2013, 08:08:03 PM »
Tony,

What about using the HashSet built-in UnionWith() and ExceptWith() method instead of defining the AddRange() and RemoveRange() ones ?

Hi Gile, yes you can do that, but I've not checked to see if the HashSet<T> has those methods in earlier versions of the framework. 

I use both the DisposableSet<T> and a similar class called DisposableList<T>, which both have those  methods because they both support a common interface called IDisposableCollection<T>, which allows me to use either the list or hashset without the consuming code being directly dependent on any implementing class. The IDisposableCollection<T> interface is where the AddRange() and RemoveRange() methods are, so I implement them on both the DisposableSet<T> and DisposableList<T> class that both support that same interface. In the version I posted, I omitted the IDisposableCollection<T> interface only for brevity.

In any case, we can use UnionWith() and ExceptWith(), but still use the AddRange() and RemoveRange() wrappers that can delegate to them, and exploit the latter by having it return its argument, which can then be returned by its caller, making the entire thing much more succinct:

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5.  
  6. namespace OffsetPolylineSample
  7. {
  8.    /// <summary>
  9.    /// Provides the Offset() extension method for the Polyline type
  10.    /// </summary>
  11.    public static class PolylineExtension
  12.    {
  13.       /// <summary>
  14.       /// Enumeration of offset side options
  15.       /// </summary>
  16.       public enum OffsetSide
  17.       {
  18.          In, Out, Left, Right, Both
  19.       }
  20.  
  21.       /// <summary>
  22.       /// Offset the source polyline to specified side(s).
  23.       /// </summary>
  24.       /// <param name="source">The polyline to be offseted.</param>
  25.       /// <param name="offsetDist">The offset distance.</param>
  26.       /// <param name="side">The offset side(s).</param>
  27.       /// <returns>A polyline sequence resulting from the offset of the source polyline.</returns>
  28.       public static IEnumerable<Polyline> Offset( this Polyline source, double offsetDist, OffsetSide side )
  29.       {
  30.          offsetDist = Math.Abs( offsetDist );
  31.          using( var plines = new DisposableSet<Polyline>() )
  32.          {
  33.             IEnumerable<Polyline> offsetRight = source.GetOffsetCurves( offsetDist ).Cast<Polyline>();
  34.             plines.AddRange( offsetRight );
  35.             IEnumerable<Polyline> offsetLeft = source.GetOffsetCurves( -offsetDist ).Cast<Polyline>();
  36.             plines.AddRange( offsetLeft );
  37.             double areaRight = offsetRight.Select( pline => pline.Area ).Sum();
  38.             double areaLeft = offsetLeft.Select( pline => pline.Area ).Sum();
  39.             switch( side )
  40.             {
  41.                case OffsetSide.In:
  42.                   return plines.RemoveRange(
  43.                      areaRight < areaLeft ? offsetRight : offsetLeft );
  44.                case OffsetSide.Out:
  45.                   return plines.RemoveRange(
  46.                      areaRight < areaLeft ? offsetLeft : offsetRight );
  47.                case OffsetSide.Left:
  48.                   return plines.RemoveRange( offsetLeft );
  49.                case OffsetSide.Right:
  50.                   return plines.RemoveRange( offsetRight );
  51.                case OffsetSide.Both:
  52.                   plines.Clear();
  53.                   return offsetRight.Concat( offsetLeft );
  54.                default:
  55.                   return null;
  56.             }
  57.          }
  58.       }
  59.    }
  60.  
  61.    public interface IDisposableCollection<T> : ICollection<T>, IDisposable
  62.       where T : IDisposable
  63.    {
  64.       void AddRange( IEnumerable<T> items );
  65.       IEnumerable<T> RemoveRange( IEnumerable<T> items );
  66.    }
  67.  
  68.    public class DisposableSet<T> : HashSet<T>, IDisposableCollection<T>
  69.       where T: IDisposable
  70.    {
  71.       public DisposableSet()
  72.       {
  73.       }
  74.  
  75.       public DisposableSet( IEnumerable<T> items )
  76.       {
  77.          AddRange( items );
  78.       }
  79.  
  80.       public void Dispose()
  81.       {
  82.          if( base.Count > 0 )
  83.          {
  84.             System.Exception last = null;
  85.             var list = this.ToList();
  86.             this.Clear();
  87.             foreach( T item in list )
  88.             {
  89.                if( item != null )
  90.                {
  91.                   try
  92.                   {
  93.                      item.Dispose();
  94.                   }
  95.                   catch( System.Exception ex )
  96.                   {
  97.                      last = last ?? ex;
  98.                   }
  99.                }
  100.             }
  101.             if( last != null )
  102.                throw last;
  103.          }
  104.       }
  105.  
  106.       public void AddRange( IEnumerable<T> items )
  107.       {
  108.          if( items == null )
  109.             throw new ArgumentNullException( "items" );
  110.          base.UnionWith( items );
  111.       }
  112.  
  113.       public IEnumerable<T> RemoveRange( IEnumerable<T> items )
  114.       {
  115.          if( items == null )
  116.             throw new ArgumentNullException( "items" );
  117.          base.ExceptWith( items );
  118.          return items;
  119.       }
  120.    }
  121.  
  122. }
  123.  
  124.  
« Last Edit: March 18, 2013, 08:29:21 PM by TT »