@Activist_Investor wrote:
See the comments in the code...
I broke the above code while cleaning it up to post.
This one comes with a limited 30 day money-back guarantee.
using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using AcRx = Autodesk.AutoCAD.Runtime;
namespace ConvertToPolyline
{
public static class ConvertToPolylineCommands
{
/// <summary>
/// Replaces a selected Line or Arc with an equivalent Polyline.
/// The polyline that replaces the Line or Arc inherits the
/// handle, application data, and common properties of the
/// Line or Arc.
/// </summary>
[CommandMethod("CVPOLY")]
public static void ConvertToPolyLineCommand()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
PromptEntityOptions peo = new PromptEntityOptions("\nSelect a Line or Arc: ");
peo.SetRejectMessage("\nInvalid input, requires a Line or Arc,\n");
peo.AddAllowedClass(typeof(Line), false);
peo.AddAllowedClass(typeof(Arc), false);
peo.AllowObjectOnLockedLayer = false;
var per = doc.Editor.GetEntity(peo);
if(per.Status != PromptStatus.OK)
return;
using(var trans = doc.TransactionManager.StartOpenCloseTransaction())
{
Curve curve = (Curve) trans.GetObject(per.ObjectId, OpenMode.ForRead);
try
{
curve.ReplaceWithPolyline(trans);
trans.Commit();
}
catch(System.Exception ex)
{
doc.Editor.WriteMessage(ex.ToString());
}
}
}
}
}
namespace Autodesk.AutoCAD.DatabaseServices
{
public static class ConvertToPolylineExtensions
{
/// <summary>
/// Replaces the given database-resident Line or Arc
/// with an equivalent polyline
/// </summary>
public static Polyline ReplaceWithPolyline(this Curve curve, OpenCloseTransaction trans)
{
return Convert(curve, trans);
}
/// <summary>
/// Creates a new Polyline that is equivalent to the given
/// Line or Arc.
///
/// The resulting Polyline is not database-resident and
/// must be disposed or added to a database.
/// </summary>
public static Polyline ConvertToPolyline(this Curve curve)
{
return Convert(curve);
}
/// <summary>
/// Creates a Polyline that is equivalent to a given Arc or Line,
/// and optionally replaces the Arc or Line with the Polyline in
/// the Database where the Arc or Line resides.
/// </summary>
/// <remarks>The curve argument must be a Line or Arc. If the curve
/// argument is database-resident and the second transaction argument
/// is provided, the curve MUST be opened via that transaction.
///
/// If the curve argument is database-resident and a transaction is
/// provided, the new Polyline will replace the curve in the database
/// and inherit the common entity properties, handle, and application
/// data of the curve. The curve argument will no longer be usable
/// upon return. If the curve argument is not database-resident, the
/// resulting polyline will not be database-resident and <em>must</em>
/// be disposed or added to a database. The curve argument will be
/// unchanged and remain usable.
/// </remarks>
/// <param name="curve">The Line or Arc to convert/replace</param>
/// <param name="trans">The OpenCloseTransaction which the database-
/// resident curve was obtained from.</param>
/// <returns>The Polyline equivalent of the given curve</returns>
static Polyline Convert(Curve curve, OpenCloseTransaction trans = null)
{
if(curve == null)
throw new ArgumentNullException("curve");
if(!(curve is Line || curve is Arc))
ErrorStatus.WrongObjectType.Throw("requires a Line or Arc");
if(curve.IsTransactionResident)
ErrorStatus.InvalidInput.Throw("curve must be from an OpenCloseTransaction");
Polyline pline = new Polyline(1);
try
{
Line line = null;
Arc arc = curve as Arc;
if(arc != null)
{
pline.Thickness = arc.Thickness;
pline.Normal = arc.Normal;
}
else
{
line = (Line) curve;
pline.Thickness = line.Thickness;
/// Special case: Lines that are not perpendicular to
/// their normal/extrusion direction are handled in a
/// peculiar way by PEDIT. I haven't had the time to
/// reverse-engineer it fully, but it seems that PEDIT
/// simply takes the line as a vector and rotates it
/// 90 degrees (Vector3d.GetPerpendicularVector()),
/// and uses that as the polyline's normal, which is
/// not the same as the line's extrusion direction,
/// which produces seemingly-odd behavior that is only
/// obvious if the Line has a non-zero thickness.
///
/// Drawing a line in the WCS with endpoints that are
/// at different elevations, and giving it a non-zero
/// thickness will reveal what PEDIT does in cases like
/// this (not what a user might expect).
///
/// Unfortunately, JoinEntity() does not like lines that
/// have a non-perpendicular extrusion vector/normal, and
/// as such, can't be used to convert any such line into
/// a polyline. So, just compute the normal and add both
/// the start and end vertices, thereby avoiding the use
/// of JoinEntity() entirely.
///
/// This iteraton should more-closely emulate the PEDIT
/// command's behavior (the initial intent of this code)
/// when an arc or line is selected:
Vector3d normal = line.Normal;
Vector3d vector = line.StartPoint.GetVectorTo(line.EndPoint);
if(!vector.IsPerpendicularTo(normal))
normal = vector.GetPerpendicularVector();
pline.Normal = normal;
}
Point3d startPoint = curve.StartPoint.TransformBy(pline.Ecs.Inverse());
Point3d endPoint = curve.EndPoint.TransformBy(pline.Ecs.Inverse());
pline.Elevation = startPoint.Z;
pline.AddVertexAt(0, new Point2d(startPoint.X, startPoint.Y), 0, 0, 0);
if(line != null)
pline.AddVertexAt(1, new Point2d(endPoint.X, endPoint.Y), 0, 0, 0);
else
pline.JoinEntity(curve);
pline.SetPropertiesFrom(curve);
if(curve.Database != null && trans != null)
{
if(!curve.IsWriteEnabled)
curve.UpgradeOpen();
curve.HandOverTo(pline, true, true);
trans.AddNewlyCreatedDBObject(pline, true);
trans.AddNewlyCreatedDBObject(curve, false);
curve.Dispose();
}
return pline;
}
catch
{
pline.Dispose();
throw;
}
}
public static void Throw(this AcRx.ErrorStatus es, string message = null)
{
throw new AcRx.Exception(es, message);
}
}
}