A question came in by email, yesterday, and despite having a few mini-projects in progress that were spawned during last week’s event in Prague, I couldn’t resist putting some code together to address it.
The developer wanted to purge zero-length geometry: the simplest way to solve the problem is to run “_-PURGE _Z” at the command-line, but I saw this more as an opportunity to create a simple helper that would loop through all the entities in a drawing and erase any meeting a specified condition. In this case it would be curves with a length less than the global tolerance, but the same mechanism could certainly be used to erase geometry meeting other conditions, instead.
Here’s some C# code that does this:
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>
/// 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
)
{
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))
{
if (!countOnly)
{
ent.UpgradeOpen();
ent.Erase();
ent.DowngradeOpen();
}
count++;
}
}
}
tr.Commit();
}
return count;
}
}
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 =>
{
var cur = e as Curve;
return
cur != null &&
cur.GetDistanceAtParameter(cur.EndParam) <
Tolerance.Global.EqualPoint;
}
);
ed.WriteMessage("\nErased {0} entities.", count);
}
}
}
The code just loops through each of the blocks in the drawing’s block table and checks its contents against a condition specified by a lambda function. The function takes one argument – an entity – and returns a Boolean indicating whether it deserves to be deleted or gets to live on.
After looking at the code for a few minutes, I realised it probably makes more sense to generalise the extension method even further, having it apply a generic operation to every entity in the drawing. This is the version we’ll see in the next post, for comparison.