A question came into the LISP forum during Wednesday’s Answer Day. It related to a really interesting task: to take certain blocks in a drawing and connect them to the closest polyline via a perpendicular line. It took me a little while to understand, but basically the problem relates to pipeline design: there are gully posts – represented as blocks – that need to be connected to pipelines (polylines) in the drawing. They need to be connected by the shortest path, which will be perpendicular to the pipeline (assuming the pipeline is long enough, of course).
It was too juicy a problem to put to one side, so I ended up putting a little code together. I chose to solve it using C#: LISP wasn’t a hard and fast requirement, and it’s far easier for me to code it in .NET. The answer I originally gave on the forum was unnecessarily complicated, as it turns out, but we’ll see that on Monday when we extend this code to also search for pipelines in the drawing.
So, to start with, let’s see some C# code that asks the user to select a block and a curve, and we connect the two via a line. As requested, the code takes the insertion point of the block for the initial connection point.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
namespace ConnectBlocksToCurves
{
public static class Extensions
{
public static double DistanceFrom(this Curve c, Point3d pt)
{
return pt.DistanceTo(c.GetClosestPointTo(pt, false));
}
public static void ConnectPointToCurve(
this Transaction tr, BlockTableRecord btr,
Point3d pt, Curve c
)
{
tr.DrawLine(btr, pt, c.GetClosestPointTo(pt, false));
}
public static void DrawLine(
this Transaction tr, BlockTableRecord btr, Point3d pt1, Point3d pt2
)
{
var ln = new Line(pt1, pt2);
btr.AppendEntity(ln);
tr.AddNewlyCreatedDBObject(ln, true);
}
}
public class Commands
{
[CommandMethod("B2C")]
public static void Block2Curve()
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
return;
var db = doc.Database;
var ed = doc.Editor;
// Get the block to connect
var peo = new PromptEntityOptions("\nSelect the block");
peo.SetRejectMessage("\nMust be a block.");
peo.AddAllowedClass(typeof(BlockReference), false);
var per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
var brId = per.ObjectId;
// Get the curve to connect it to
var peo2 = new PromptEntityOptions("\nSelect the curve");
peo2.SetRejectMessage("\nMust be a curve.");
peo2.AddAllowedClass(typeof(Curve), false);
var per2 = ed.GetEntity(peo2);
if (per2.Status != PromptStatus.OK)
return;
var cId = per2.ObjectId;
// Use a transaction for the geometry access and connection creation
using (var tr = doc.TransactionManager.StartTransaction())
{
var br = tr.GetObject(brId, OpenMode.ForRead) as BlockReference;
var c = tr.GetObject(cId, OpenMode.ForRead) as Curve;
if (br != null && c != null)
{
// We'll be writing to the modelspace
var btr =
(BlockTableRecord)tr.GetObject(
SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite
);
// Connect the point to the closest point on the curve
tr.ConnectPointToCurve(btr, br.Position, c);
}
tr.Commit();
}
}
}
}
Here’s the code in action:
On Monday we’ll extend this to connect any block of the selected type to the nearest curve of any type in the drawing. Which is thankfully much simpler than it sounds.