Applying an operation to all the entities in an AutoCAD drawing using .NET

In the last post we saw a "general" function to erase all the entities in a drawing that fulfill a specified condition. We used it to erase all the zero-length lines in a drawing. But as I'd mentioned at the end, I thought there was an opportunity to generalize the mechanism even further. Here's what I came up with (and I've included a suggestion by Parrish Husband to create an additional extension method for Curve.IsZeroLength()).

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System;

 

namespace ZeroLengthEntities

{

  public static class Extensions

  {

    /// <summary>

    /// Performs an operation on all entities found in this database.

    /// </summary>

    /// <param name="f">Function performing an operation on an entity.</param>

    /// <returns>The number of times the function returned true.</returns>

 

    public static int ForEachEntity(this Database db, Func<Entity, bool> f)

    {

      int count = 0;

      using (var tr = db.TransactionManager.StartTransaction())

      {

        var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

        foreach (ObjectId btrId in bt)

        {

          var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);

          foreach (ObjectId entId in btr)

          {

            var ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;

            if (ent != null && f(ent))

            {

              count++;

            }

          }

        }

        tr.Commit();

      }

      return count;

    }

 

    /// <summary>

    /// Erases entities found in this database that fulfill a condition.

    /// </summary>

    /// <param name="f">Function indicating whether an enity needs erasing.</param>

    /// <param name="countOnly">Optional parameter to count but not erase.</param>

    /// <returns>The number of entities found (and - if !countOnly - erased).</returns>

 

    public static int ConditionalErase(

      this Database db, Func<Entity, bool> f, bool countOnly = false

    )

    {

      return

        db.ForEachEntity(

          e =>

          {

            if (f(e))

            {

              if (!countOnly)

              {

                e.UpgradeOpen();

                e.Erase();

                e.DowngradeOpen();

              }

              return true;

            }

            return false;

          }

        );

    }

 

    /// <summary>

    /// Returns the length of a curve.

    /// </summary>

    /// <returns>The length of this curve.</returns>

 

    public static double Length(this Curve cur)

    {

      return cur.GetDistanceAtParameter(cur.EndParam);

    }

 

    /// <summary>

    /// Checks whether a curve is of zero length.

    /// </summary>

    /// <returns>A Boolean indicating whether this curve is of zero length.</returns>

 

    public static bool IsZeroLength(this Curve cur)

    {

      return cur.Length() < Tolerance.Global.EqualPoint;

    }

  }

 

  public class Commands

  {

    [CommandMethod("EZL")]

    public void EraseZeroLength()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

      var db = doc.Database;

      var ed = doc.Editor;

 

      var count = db.ConditionalErase(

        e =>

        {

          if (e is Curve)

            return ((Curve)e).IsZeroLength();

          return false;

        }

      );

 

      ed.WriteMessage("\nErased {0} entities.", count);

    }

 

    [CommandMethod("EZL2")]

    public void EraseZeroLength2()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc ==
null
)

        return;

      var db = doc.Database;

      var ed = doc.Editor;

 

      var count = db.ForEachEntity(

        e =>

        {

          var cur = e as Curve;

          if (cur != null && cur.IsZeroLength())

          {

            e.UpgradeOpen();

            e.Erase();

            e.DowngradeOpen();

 

            return true;

          }

          return false;

        }

      );

 

      ed.WriteMessage("\nErased {0} entities.", count);

    }

  }

}

 

The code operates in exactly the same way as last time, but we now have a Database.ForEachEntity() method that is used by Database.ConditionalErase() but can also be used directly to perform read/update/delete operations on all the entities in a drawing. In fact I've already used it in a separate project – that we'll see next week – to "touch" all the entities in a drawing in order to force a redraw.

You can compare the implementations of the functionally equivalent commands, EZL and EZL2. See which style you prefer.

As a reminder, from last time, here's what the EZL and EZL2 commands do when you run them:

Erasing zero-length lines

Leave a Reply

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