Solved! Go to Solution.
Solved! Go to Solution.
Solved by _gile. Go to Solution.
[CommandMethod("myb")]
public static void MyBreakLine()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions opt1 = new PromptEntityOptions("\nselect a line:");
opt1.SetRejectMessage("\nerror!");
opt1.AddAllowedClass(typeof(Line), true);
PromptEntityResult res1 = ed.GetEntity(opt1);
if (res1.Status == PromptStatus.OK)
{
PromptPointOptions opt2 = new PromptPointOptions("\nselect second point:");
opt2.AllowNone = true;
PromptPointResult res2 = ed.GetPoint(opt2);
using (Transaction tr = db.TransactionManager.StartTransaction())
{
Line l = (Line)tr.GetObject(res1.ObjectId, OpenMode.ForRead);
List<double> pars = new List<double>();
Point3d pt1 = l.GetClosestPointTo(res1.PickedPoint, false);
Point3d pt2 = new Point3d();
pars.Add(l.GetParameterAtPoint(pt1));
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite,
false);
if (res2.Status == PromptStatus.OK)
{
pt2 = l.GetClosestPointTo(res2.Value, false);
pars.Add(l.GetParameterAtPoint(pt2));
pars.Sort();
DBObjectCollection objs = l.GetSplitCurves(new DoubleCollection(pars.ToArray()));
foreach (Line ll in objs)
{
if ((ll.StartPoint != pt1 && ll.StartPoint != pt2) ^ (ll.EndPoint != pt1 && ll.EndPoint != pt2))
{
btr.AppendEntity(ll);
tr.AddNewlyCreatedDBObject(ll, true);
}
}
}
else
{
DBObjectCollection objs = l.GetSplitCurves(new DoubleCollection(pars.ToArray()));
foreach (Line ll in objs)
{
btr.AppendEntity(ll);
tr.AddNewlyCreatedDBObject(ll, true);
}
}
l.UpgradeOpen();
l.Erase();
tr.Commit();
}
}
}
[CommandMethod("myb")]
public static void MyBreakLine()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions opt1 = new PromptEntityOptions("\nselect a line:");
opt1.SetRejectMessage("\nerror!");
opt1.AddAllowedClass(typeof(Line), true);
PromptEntityResult res1 = ed.GetEntity(opt1);
if (res1.Status == PromptStatus.OK)
{
PromptPointOptions opt2 = new PromptPointOptions("\nselect second point:");
opt2.AllowNone = true;
PromptPointResult res2 = ed.GetPoint(opt2);
using (Transaction tr = db.TransactionManager.StartTransaction())
{
Line l = (Line)tr.GetObject(res1.ObjectId, OpenMode.ForRead);
List<double> pars = new List<double>();
Point3d pt1 = l.GetClosestPointTo(res1.PickedPoint, false);
Point3d pt2 = new Point3d();
pars.Add(l.GetParameterAtPoint(pt1));
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite,
false);
if (res2.Status == PromptStatus.OK)
{
pt2 = l.GetClosestPointTo(res2.Value, false);
pars.Add(l.GetParameterAtPoint(pt2));
pars.Sort();
DBObjectCollection objs = l.GetSplitCurves(new DoubleCollection(pars.ToArray()));
foreach (Line ll in objs)
{
if ((ll.StartPoint != pt1 && ll.StartPoint != pt2) ^ (ll.EndPoint != pt1 && ll.EndPoint != pt2))
{
btr.AppendEntity(ll);
tr.AddNewlyCreatedDBObject(ll, true);
}
}
}
else
{
DBObjectCollection objs = l.GetSplitCurves(new DoubleCollection(pars.ToArray()));
foreach (Line ll in objs)
{
btr.AppendEntity(ll);
tr.AddNewlyCreatedDBObject(ll, true);
}
}
l.UpgradeOpen();
l.Erase();
tr.Commit();
}
}
}
Hi,
A code which you have Posted here as Breaking Line/Polyline. where as Its supporing only for Polylines not for Lines. So Please look into this, It should support for Lines and Polylines both.
The Reason may be below,
Line l= (Line)tr.GetObject(res1.ObjectId, OpenMode.ForRead);
Regards
Alex Andrews.
Hi,
A code which you have Posted here as Breaking Line/Polyline. where as Its supporing only for Polylines not for Lines. So Please look into this, It should support for Lines and Polylines both.
The Reason may be below,
Line l= (Line)tr.GetObject(res1.ObjectId, OpenMode.ForRead);
Regards
Alex Andrews.
Hi,
This one mimics the native BREAK command.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .Distinct() .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); curve.Erase(); break; case 2: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); curve.Erase(); break; default: break; } tr.Commit(); } }
Hi,
This one mimics the native BREAK command.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .Distinct() .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); curve.Erase(); break; case 2: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); curve.Erase(); break; default: break; } tr.Commit(); } }
Thank You so much Gilles!!! Its working fine as per my request.
Alex Andrews.
Thank You so much Gilles!!! Its working fine as per my request.
Alex Andrews.
Hi @_gile,
To fully-mimic the native BREAK command, the code must honor application data and inter-object references that can be established with most any type of AutoCAD entity (this holds especially true with the use of constraints), so rather than erasing the original object, and appending the replacement object(s), You can use HandOverTo() on the original object and the first replacement object, because that preserves both application data and inter-object references.
One other minor point about your code is that there's no need for calling Distinct() on points or parameters passed to GetSplitCurves(). It will ignore duplicates and also points or parameters that correspond to the start/end points/parameters. I only recently discovered that myself after reading the native ObjectARX docs and doing some testing.
And lastly, like most example code posted here (including my own), your code will work on entities that lie in the XY plane of the current UCS, in a PLAN view of same, but can easily fail if the entities involved do not lie in the current UCS XY plane, and/or the current view is not a plan view of same. The reason is because the point returned by GetEntity() is always in the XY plane of the current UCS, which may not be the closest point to the selected curve in the DCS.
Here's the extension method I use to properly find the point on a curve, closest to the point where the curve was picked:
public static class EditorExtensions { /// <summary> /// Returns the point on the given curve that is closest to /// a line that's parallel to the current view direction, /// and passes through the given point. The given point is /// a point expressed in the current UCS. The resulting /// point is expressed in the WCS. /// </summary> /// /// <remarks>This API must be called immediately after the /// given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., in cases where there /// are multiple points being obtained, and the user is able /// to switch viewports and specify points in any viewport. /// </remarks> public static Point3d GetClosestPointTo(this Editor editor, Curve curve, Point3d ucsPoint) { return curve.GetClosestPointTo(ucsPoint.TransformBy(editor.CurrentUserCoordinateSystem), editor.GetCurrentView().ViewDirection, false); } }
@_gile wrote:Hi,
This one mimics the native BREAK command.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .Distinct() .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); curve.Erase(); break; case 2: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); curve.Erase(); break; default: break; } tr.Commit(); } }
Hi @_gile,
To fully-mimic the native BREAK command, the code must honor application data and inter-object references that can be established with most any type of AutoCAD entity (this holds especially true with the use of constraints), so rather than erasing the original object, and appending the replacement object(s), You can use HandOverTo() on the original object and the first replacement object, because that preserves both application data and inter-object references.
One other minor point about your code is that there's no need for calling Distinct() on points or parameters passed to GetSplitCurves(). It will ignore duplicates and also points or parameters that correspond to the start/end points/parameters. I only recently discovered that myself after reading the native ObjectARX docs and doing some testing.
And lastly, like most example code posted here (including my own), your code will work on entities that lie in the XY plane of the current UCS, in a PLAN view of same, but can easily fail if the entities involved do not lie in the current UCS XY plane, and/or the current view is not a plan view of same. The reason is because the point returned by GetEntity() is always in the XY plane of the current UCS, which may not be the closest point to the selected curve in the DCS.
Here's the extension method I use to properly find the point on a curve, closest to the point where the curve was picked:
public static class EditorExtensions { /// <summary> /// Returns the point on the given curve that is closest to /// a line that's parallel to the current view direction, /// and passes through the given point. The given point is /// a point expressed in the current UCS. The resulting /// point is expressed in the WCS. /// </summary> /// /// <remarks>This API must be called immediately after the /// given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., in cases where there /// are multiple points being obtained, and the user is able /// to switch viewports and specify points in any viewport. /// </remarks> public static Point3d GetClosestPointTo(this Editor editor, Curve curve, Point3d ucsPoint) { return curve.GetClosestPointTo(ucsPoint.TransformBy(editor.CurrentUserCoordinateSystem), editor.GetCurrentView().ViewDirection, false); } }
@_gile wrote:Hi,
This one mimics the native BREAK command.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .Distinct() .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); curve.Erase(); break; case 2: curSpace.AppendEntity((Entity)curves[0]); tr.AddNewlyCreatedDBObject(curves[0], true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); curve.Erase(); break; default: break; } tr.Commit(); } }
Thanks for these clafications.
I thaught about HandOverTo() (and also the curve plane and current view) but I wanted to keep the code focused on the @Anonymous request.
Thanks also for your extension methods.
Thanks for these clafications.
I thaught about HandOverTo() (and also the curve plane and current view) but I wanted to keep the code focused on the @Anonymous request.
Thanks also for your extension methods.
@_gile wrote:
Thanks for these clafications.
I thaught about HandOverTo() (and also the curve plane and current view) but I wanted to keep the code focused on the @Anonymous request.
Thanks also for your extension methods.
You're welcome @_gile. I only mentioned those things because I had been bitten by the same 'selection in non-plan view problem' recently also, and was also under assumption that Distinct() must be used to remove duplicate points/parameters.
@_gile wrote:
Thanks for these clafications.
I thaught about HandOverTo() (and also the curve plane and current view) but I wanted to keep the code focused on the @Anonymous request.
Thanks also for your extension methods.
You're welcome @_gile. I only mentioned those things because I had been bitten by the same 'selection in non-plan view problem' recently also, and was also under assumption that Distinct() must be used to remove duplicate points/parameters.
A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
@_gile wrote:A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
@_gile wrote:A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
Activist_Investor a écrit :
Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
Thanks,
I just noticed a warning in th VS Output window:
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.ViewTableRecord): DisposableWrapper
Activist_Investor a écrit :
Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
Thanks,
I just noticed a warning in th VS Output window:
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.ViewTableRecord): DisposableWrapper
@_gile, please read again the remarks from my GetClosestPointTo() extension method:
/// <remarks>This API must be called immediately after the /// given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., in cases where there /// are multiple points being obtained, and the user is able /// to switch viewports and specify points in any viewport. /// </remarks>
And, see my annotations of your code:
@_gile wrote:A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; ////////////////////////////////////////////////////////////// // viewDir is valid for the PickedPoint of the entityResult ////////////////////////////////////////////////////////////// var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); ///////////////////////////////////////////////////////////// // At the above prompt from GetPoint(), the user can // change the active viewport, or transparently orbit // the current view before picking the point, making // the above viewDir invalid for the selected point. ///////////////////////////////////////////////////////////// if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions);
///////////////////////////////////////////////////////////////// // Ditto as per above for the second call to GetPoint(), // the viewDir is not valid for computing a point on the curve /////////////////////////////////////////////////////////////////
if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
This is one reason why I chose to take a modular route for solving this problem, and my own library takes it a bit further..
Below are surrogates for GetEntity() and GetPoint() that encapsulate both the selection, and computing the point on the selected curve, so that it is done at the point when it must be done (immediately after the user responds to the prompt, while the view they used to pick the entity or point is unchanged).
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Linq.Expressions; using System.MyDiagnostics; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; #pragma warning disable 0618 namespace Autodesk.AutoCAD.EditorInput { public static class EditorExtensions { /// <summary> /// Returns the WCS point on the given curve that is closest /// to a line parallel to the current view direction, passing /// through the given point. The given point must be expressed /// in current UCS coordinates, and is typically obtained from /// an instance of PromptResult-based type returned by a call /// to one of the Editor's GetXxxxx() methods. /// /// The resulting point is a World coordinate. /// </summary> /// <remarks>This API must be called <em>immediately</em> after /// the given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., while the viewport which /// the point or object was selected in is still active, and the /// view parameters of that viewport have not changed. /// /// This method produces the 'apparent' nearest point on the /// curve, accounting for the curve's orientation in 3D space, /// the current view direction, and the current UCS. /// </remarks> public static Point3d GetClosestPointTo(this Editor editor, Curve curve, Point3d ucsPoint) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(curve, "curve"); using(var view = editor.GetCurrentView()) { return curve.GetClosestPointTo( ucsPoint.TransformBy(editor.CurrentUserCoordinateSystem), view.ViewDirection, false); } } /// <summary> /// Like GetEntity(), except that if the selected entity is a curve, /// the PickedPoint is the WCS point on the curve closest to a line /// parallel to the view direction, passing through the point used /// to pick the curve. /// /// The PickedPoint property of the result <em>is a WCS coordinate</em>. /// </summary> /// <remarks> /// This method encapsulates the process of selecting an entity and /// if it is a curve, computing the point on the curve nearest to the /// selection point, to ensure that the correct view parameters are /// used in the computation. /// /// This method does not restrict selection to a Curve-based type, as /// it is designed to work with any type derived from Curve. Therefore, /// the caller should initialize the options argument as needed to /// restrict input to a Curve-based type, using AddAllowedClass() and /// SetRejectMessage(). /// </remarks> public static PromptEntityResult GetCurve(this Editor editor, PromptEntityOptions options, bool setLastPoint = false) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(options, "options"); var result = editor.GetEntity(options); if(result.Status == PromptStatus.OK && result.ObjectId.ObjectClass.IsDerivedFrom(curveClass)) { using(Curve curve = (Curve) result.ObjectId.Open(OpenMode.ForRead)) { var rawPoint = result.PickedPoint; result.SetPickPoint(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } } return result; } public static PromptEntityResult GetCurve(this Editor editor, string message) { return GetCurve(editor, new PromptEntityOptions(message)); } /// <summary> /// Like GetPoint(), except can be used when requesting a point that /// lies on a Curve. The PromptPointResult will contain the WCS point /// on the curve, nearest to the selection point in the DCS. It will /// optionally set the LASTPOINT system variable to the 'raw' picked /// point. /// /// The Value property of the result <em>is a World coordinate</em>. /// </summary> /// <remarks> /// This method encapsulates the process of obtaining a point on a /// curve and computing that point to ensure that the correct view /// parameters are used in the computation. /// </remarks> /// <param name="setLastPoint">True to set the LASTPOINT system /// variable to the actual UCS point that was selected.</param> /// <returns></returns> public static PromptPointResult GetPointOn(this Editor editor, Curve curve, PromptPointOptions options, bool setLastPoint = false) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(options, "ppo"); Assert.IsNotNull(curve, "curve"); var result = editor.GetPoint(options); if(result.Status == PromptStatus.OK) { var rawPoint = result.Value; result.SetValue(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } return result; } /// <summary> /// Prompts user for a point and returns the WCS point on the Curve /// whose ObjectId is the given curveId, nearest to a line passing /// through the user-selected point, and parallel to the current /// view direction. /// /// This method encapsulates the process of obtaining a point on a /// curve and computing that point to ensure that the correct view /// parameters are used in the computation. /// /// The Value property of the result <em>is a World coordinate</em>. /// </summary> public static PromptPointResult GetPointOn(this Editor editor, ObjectId curveId, PromptPointOptions options, bool setLastPoint = false) { ErrorStatus.NullObjectId.Check(!curveId.IsNull); ErrorStatus.WrongObjectType.Check(curveId.ObjectClass.IsDerivedFrom(curveClass)); var result = editor.GetPoint(options); if(result.Status == PromptStatus.OK) { using(Curve curve = (Curve) curveId.Open(OpenMode.ForRead)) { Point3d rawPoint = result.Value; result.SetValue(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } } return result; } public static PromptPointResult GetPointOn(this Editor editor, ObjectId curveId, string message, bool setLastPoint = false) { Assert.IsNotNullOrWhiteSpace(message, "message"); return GetPointOn(editor, curveId, new PromptPointOptions(message), setLastPoint); } public static PromptPointResult GetPointOn(this Editor editor, Curve curve, string message, bool setLastPoint = false) { Assert.IsNotNullOrWhiteSpace(message, "message"); return GetPointOn(editor, curve, new PromptPointOptions(message), setLastPoint); } static void SetValue(this PromptPointResult ppr, Point3d point) { ppr_set_value(ppr, point); } static void SetPickPoint(this PromptEntityResult per, Point3d point) { per_set_pickPoint(per, point); } static Action<PromptPointResult, Point3d> ppr_set_value = FieldAccess.GetModifier<PromptPointResult, Point3d>("m_value"); static Action<PromptEntityResult, Point3d> per_set_pickPoint = FieldAccess.GetModifier<PromptEntityResult, Point3d>("m_pickPoint"); static RXClass curveClass = RXObject.GetClass(typeof(Curve)); } } namespace System.Reflection { /// <summary> /// Methods for generating delegates that access and /// modify fields without relying on reflection. /// </summary> public static class FieldAccess { public static Func<TComponent, TField> GetAccessor<TComponent, TField>(string name, BindingFlags flags = defaultFlags) { return (Func<TComponent, TField>) GetAccessor(typeof(TComponent), typeof(TField), name, flags); } public static Action<TComponent, TField> GetModifier<TComponent, TField>(string name, BindingFlags flags = defaultFlags) { return (Action<TComponent, TField>) GetModifier(typeof(TComponent), typeof(TField), name, flags); } public static Delegate GetModifier(this Type declaringType, Type fieldType, string name, BindingFlags flags = defaultFlags) { Assert.IsNotNull(declaringType, "declaringType"); Assert.IsNotNull(fieldType, "fieldType"); Assert.IsNotNullOrWhiteSpace(name, "name"); FieldInfo field = declaringType.GetField(name, flags); if(field == null) Throw(declaringType, name, "not found"); var instance = Expression.Parameter(declaringType, "instance"); var value = Expression.Parameter(fieldType, "value"); var assign = Expression.Assign(Expression.Field(instance, field), value); return Expression.Lambda(assign, instance, value).Compile(); } public static Delegate GetAccessor(this Type declaringType, Type fieldType, string name, BindingFlags flags = defaultFlags) { Assert.IsNotNull(declaringType, "declaringType"); Assert.IsNotNull(fieldType, "fieldType"); Assert.IsNotNullOrWhiteSpace(name, "name"); FieldInfo field = declaringType.GetField(name, flags); if(field == null) Throw(declaringType, name, "not found"); if(!fieldType.IsAssignableFrom(field.FieldType)) Throw(declaringType, name, "type mismatch"); ParameterExpression instance = Expression.Parameter(declaringType); return Expression.Lambda(Expression.Field(instance, field), new[] { instance }).Compile(); } const BindingFlags defaultFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; private static void Throw(Type type, string name, string msg) { throw new ArgumentException(string.Format("Field {0}.{1} {2}", type.Name, name, msg)); } } } namespace System.MyDiagnostics { public static class Assert { public static void IsNotNull<T>(T obj, string name) where T : class { if(obj == null) throw new ArgumentException(name); } public static void IsNotNullOrWhiteSpace(this string str, string name) { if(string.IsNullOrWhiteSpace(str)) throw new ArgumentException(name); } } } namespace Autodesk.AutoCAD.Runtime { public static class RuntimeExtensions { public static void Check(this ErrorStatus es, bool condition, string msg = null) { if(!condition) { if(msg == null) throw new Autodesk.AutoCAD.Runtime.Exception(es); else throw new Autodesk.AutoCAD.Runtime.Exception(es, msg); } } } }
@_gile, please read again the remarks from my GetClosestPointTo() extension method:
/// <remarks>This API must be called immediately after the /// given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., in cases where there /// are multiple points being obtained, and the user is able /// to switch viewports and specify points in any viewport. /// </remarks>
And, see my annotations of your code:
@_gile wrote:A new version including @ActivistInvestor suggestions.
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; Point3d pt1, pt2; var ucs = ed.CurrentUserCoordinateSystem; Vector3d viewDir; using (var view = ed.GetCurrentView()) viewDir = view.ViewDirection; ////////////////////////////////////////////////////////////// // viewDir is valid for the PickedPoint of the entityResult ////////////////////////////////////////////////////////////// var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); ///////////////////////////////////////////////////////////// // At the above prompt from GetPoint(), the user can // change the active viewport, or transparently orbit // the current view before picking the point, making // the above viewDir invalid for the selected point. ///////////////////////////////////////////////////////////// if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = pointResult.Value.TransformBy(ucs); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions);
///////////////////////////////////////////////////////////////// // Ditto as per above for the second call to GetPoint(), // the viewDir is not valid for computing a point on the curve /////////////////////////////////////////////////////////////////
if (pointResult.Status != PromptStatus.OK) return; pt2 = pointResult.Value.TransformBy(ucs); } else if (pointResult.Status == PromptStatus.OK) { pt1 = entityResult.PickedPoint.TransformBy(ucs); pt2 = pointResult.Value.TransformBy(ucs); } else return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); double param1 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt1, viewDir, false)); double param2 = curve.GetParameterAtPoint(curve.GetClosestPointTo(pt2, viewDir, false)); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
This is one reason why I chose to take a modular route for solving this problem, and my own library takes it a bit further..
Below are surrogates for GetEntity() and GetPoint() that encapsulate both the selection, and computing the point on the selected curve, so that it is done at the point when it must be done (immediately after the user responds to the prompt, while the view they used to pick the entity or point is unchanged).
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Linq.Expressions; using System.MyDiagnostics; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application; #pragma warning disable 0618 namespace Autodesk.AutoCAD.EditorInput { public static class EditorExtensions { /// <summary> /// Returns the WCS point on the given curve that is closest /// to a line parallel to the current view direction, passing /// through the given point. The given point must be expressed /// in current UCS coordinates, and is typically obtained from /// an instance of PromptResult-based type returned by a call /// to one of the Editor's GetXxxxx() methods. /// /// The resulting point is a World coordinate. /// </summary> /// <remarks>This API must be called <em>immediately</em> after /// the given point is acquired via an interactive function such /// as GetPoint(), GetEntity(), etc., while the viewport which /// the point or object was selected in is still active, and the /// view parameters of that viewport have not changed. /// /// This method produces the 'apparent' nearest point on the /// curve, accounting for the curve's orientation in 3D space, /// the current view direction, and the current UCS. /// </remarks> public static Point3d GetClosestPointTo(this Editor editor, Curve curve, Point3d ucsPoint) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(curve, "curve"); using(var view = editor.GetCurrentView()) { return curve.GetClosestPointTo( ucsPoint.TransformBy(editor.CurrentUserCoordinateSystem), view.ViewDirection, false); } } /// <summary> /// Like GetEntity(), except that if the selected entity is a curve, /// the PickedPoint is the WCS point on the curve closest to a line /// parallel to the view direction, passing through the point used /// to pick the curve. /// /// The PickedPoint property of the result <em>is a WCS coordinate</em>. /// </summary> /// <remarks> /// This method encapsulates the process of selecting an entity and /// if it is a curve, computing the point on the curve nearest to the /// selection point, to ensure that the correct view parameters are /// used in the computation. /// /// This method does not restrict selection to a Curve-based type, as /// it is designed to work with any type derived from Curve. Therefore, /// the caller should initialize the options argument as needed to /// restrict input to a Curve-based type, using AddAllowedClass() and /// SetRejectMessage(). /// </remarks> public static PromptEntityResult GetCurve(this Editor editor, PromptEntityOptions options, bool setLastPoint = false) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(options, "options"); var result = editor.GetEntity(options); if(result.Status == PromptStatus.OK && result.ObjectId.ObjectClass.IsDerivedFrom(curveClass)) { using(Curve curve = (Curve) result.ObjectId.Open(OpenMode.ForRead)) { var rawPoint = result.PickedPoint; result.SetPickPoint(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } } return result; } public static PromptEntityResult GetCurve(this Editor editor, string message) { return GetCurve(editor, new PromptEntityOptions(message)); } /// <summary> /// Like GetPoint(), except can be used when requesting a point that /// lies on a Curve. The PromptPointResult will contain the WCS point /// on the curve, nearest to the selection point in the DCS. It will /// optionally set the LASTPOINT system variable to the 'raw' picked /// point. /// /// The Value property of the result <em>is a World coordinate</em>. /// </summary> /// <remarks> /// This method encapsulates the process of obtaining a point on a /// curve and computing that point to ensure that the correct view /// parameters are used in the computation. /// </remarks> /// <param name="setLastPoint">True to set the LASTPOINT system /// variable to the actual UCS point that was selected.</param> /// <returns></returns> public static PromptPointResult GetPointOn(this Editor editor, Curve curve, PromptPointOptions options, bool setLastPoint = false) { Assert.IsNotNull(editor, "editor"); Assert.IsNotNull(options, "ppo"); Assert.IsNotNull(curve, "curve"); var result = editor.GetPoint(options); if(result.Status == PromptStatus.OK) { var rawPoint = result.Value; result.SetValue(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } return result; } /// <summary> /// Prompts user for a point and returns the WCS point on the Curve /// whose ObjectId is the given curveId, nearest to a line passing /// through the user-selected point, and parallel to the current /// view direction. /// /// This method encapsulates the process of obtaining a point on a /// curve and computing that point to ensure that the correct view /// parameters are used in the computation. /// /// The Value property of the result <em>is a World coordinate</em>. /// </summary> public static PromptPointResult GetPointOn(this Editor editor, ObjectId curveId, PromptPointOptions options, bool setLastPoint = false) { ErrorStatus.NullObjectId.Check(!curveId.IsNull); ErrorStatus.WrongObjectType.Check(curveId.ObjectClass.IsDerivedFrom(curveClass)); var result = editor.GetPoint(options); if(result.Status == PromptStatus.OK) { using(Curve curve = (Curve) curveId.Open(OpenMode.ForRead)) { Point3d rawPoint = result.Value; result.SetValue(GetClosestPointTo(editor, curve, rawPoint)); if(setLastPoint) AcadApp.SetSystemVariable("LASTPOINT", rawPoint); } } return result; } public static PromptPointResult GetPointOn(this Editor editor, ObjectId curveId, string message, bool setLastPoint = false) { Assert.IsNotNullOrWhiteSpace(message, "message"); return GetPointOn(editor, curveId, new PromptPointOptions(message), setLastPoint); } public static PromptPointResult GetPointOn(this Editor editor, Curve curve, string message, bool setLastPoint = false) { Assert.IsNotNullOrWhiteSpace(message, "message"); return GetPointOn(editor, curve, new PromptPointOptions(message), setLastPoint); } static void SetValue(this PromptPointResult ppr, Point3d point) { ppr_set_value(ppr, point); } static void SetPickPoint(this PromptEntityResult per, Point3d point) { per_set_pickPoint(per, point); } static Action<PromptPointResult, Point3d> ppr_set_value = FieldAccess.GetModifier<PromptPointResult, Point3d>("m_value"); static Action<PromptEntityResult, Point3d> per_set_pickPoint = FieldAccess.GetModifier<PromptEntityResult, Point3d>("m_pickPoint"); static RXClass curveClass = RXObject.GetClass(typeof(Curve)); } } namespace System.Reflection { /// <summary> /// Methods for generating delegates that access and /// modify fields without relying on reflection. /// </summary> public static class FieldAccess { public static Func<TComponent, TField> GetAccessor<TComponent, TField>(string name, BindingFlags flags = defaultFlags) { return (Func<TComponent, TField>) GetAccessor(typeof(TComponent), typeof(TField), name, flags); } public static Action<TComponent, TField> GetModifier<TComponent, TField>(string name, BindingFlags flags = defaultFlags) { return (Action<TComponent, TField>) GetModifier(typeof(TComponent), typeof(TField), name, flags); } public static Delegate GetModifier(this Type declaringType, Type fieldType, string name, BindingFlags flags = defaultFlags) { Assert.IsNotNull(declaringType, "declaringType"); Assert.IsNotNull(fieldType, "fieldType"); Assert.IsNotNullOrWhiteSpace(name, "name"); FieldInfo field = declaringType.GetField(name, flags); if(field == null) Throw(declaringType, name, "not found"); var instance = Expression.Parameter(declaringType, "instance"); var value = Expression.Parameter(fieldType, "value"); var assign = Expression.Assign(Expression.Field(instance, field), value); return Expression.Lambda(assign, instance, value).Compile(); } public static Delegate GetAccessor(this Type declaringType, Type fieldType, string name, BindingFlags flags = defaultFlags) { Assert.IsNotNull(declaringType, "declaringType"); Assert.IsNotNull(fieldType, "fieldType"); Assert.IsNotNullOrWhiteSpace(name, "name"); FieldInfo field = declaringType.GetField(name, flags); if(field == null) Throw(declaringType, name, "not found"); if(!fieldType.IsAssignableFrom(field.FieldType)) Throw(declaringType, name, "type mismatch"); ParameterExpression instance = Expression.Parameter(declaringType); return Expression.Lambda(Expression.Field(instance, field), new[] { instance }).Compile(); } const BindingFlags defaultFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; private static void Throw(Type type, string name, string msg) { throw new ArgumentException(string.Format("Field {0}.{1} {2}", type.Name, name, msg)); } } } namespace System.MyDiagnostics { public static class Assert { public static void IsNotNull<T>(T obj, string name) where T : class { if(obj == null) throw new ArgumentException(name); } public static void IsNotNullOrWhiteSpace(this string str, string name) { if(string.IsNullOrWhiteSpace(str)) throw new ArgumentException(name); } } } namespace Autodesk.AutoCAD.Runtime { public static class RuntimeExtensions { public static void Check(this ErrorStatus es, bool condition, string msg = null) { if(!condition) { if(msg == null) throw new Autodesk.AutoCAD.Runtime.Exception(es); else throw new Autodesk.AutoCAD.Runtime.Exception(es, msg); } } } }
@_gile wrote:
Activist_Investor a écrit :Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
Thanks,
I just noticed a warning in th VS Output window:
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.ViewTableRecord): DisposableWrapper
Yes, it does that for all DBObjects.
@_gile wrote:
Activist_Investor a écrit :Nice @_gile.
I haven't noticed any problems with not disposing the ViewTableRecord returned by GetCurrentView() (perhaps because the AcDbViewTableRecord's destructor is thread-safe), but I'll revise my extension method to do that also.
Thanks,
I just noticed a warning in th VS Output window:
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.ViewTableRecord): DisposableWrapper
Yes, it does that for all DBObjects.
You're absolutely right.
What about simply using a local function?
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); var ucs = ed.CurrentUserCoordinateSystem; Point3d getPointOnCurve(Point3d pt) { using (var view = ed.GetCurrentView()) { return curve.GetClosestPointTo(pt.TransformBy(ucs), view.ViewDirection, false); } } var pt1 = getPointOnCurve(entityResult.PickedPoint); Point3d pt2; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = getPointOnCurve(pointResult.Value); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = getPointOnCurve(pointResult.Value); } else if (pointResult.Status == PromptStatus.OK) { pt2 = getPointOnCurve(pointResult.Value); } else return; double param1 = curve.GetParameterAtPoint(pt1); double param2 = curve.GetParameterAtPoint(pt2); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
You're absolutely right.
What about simply using a local function?
[CommandMethod("BRLPL")] public void BreakLineOrPolyline() { var doc = AcAp.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var entityOptions = new PromptEntityOptions("\nSelect line or polyline: "); entityOptions.SetRejectMessage("Selected object is neither a line nor a polyline."); entityOptions.AddAllowedClass(typeof(Polyline), true); entityOptions.AddAllowedClass(typeof(Line), true); var entityResult = ed.GetEntity(entityOptions); if (entityResult.Status != PromptStatus.OK) return; using (var tr = db.TransactionManager.StartOpenCloseTransaction()) { var curve = (Curve)tr.GetObject(entityResult.ObjectId, OpenMode.ForWrite); var ucs = ed.CurrentUserCoordinateSystem; Point3d getPointOnCurve(Point3d pt) { using (var view = ed.GetCurrentView()) { return curve.GetClosestPointTo(pt.TransformBy(ucs), view.ViewDirection, false); } } var pt1 = getPointOnCurve(entityResult.PickedPoint); Point3d pt2; var pointOptions = new PromptPointOptions("\nSpecify second point or [First point]: ", "First"); var pointResult = ed.GetPoint(pointOptions); if (pointResult.Status == PromptStatus.Keyword) { pointOptions.Message = "\nSpecify first point: "; pointOptions.Keywords.Clear(); pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt1 = getPointOnCurve(pointResult.Value); pointOptions.Message = "\nSpecify second point: "; pointResult = ed.GetPoint(pointOptions); if (pointResult.Status != PromptStatus.OK) return; pt2 = getPointOnCurve(pointResult.Value); } else if (pointResult.Status == PromptStatus.OK) { pt2 = getPointOnCurve(pointResult.Value); } else return; double param1 = curve.GetParameterAtPoint(pt1); double param2 = curve.GetParameterAtPoint(pt2); var parameters = new DoubleCollection (new[] { param1, param2 } .OrderBy(x => x) .ToArray()); var curves = curve.GetSplitCurves(parameters); var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); switch (curves.Count) { case 3: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[2]); tr.AddNewlyCreatedDBObject(curves[2], true); curves[1].Dispose(); break; case 2: curve.HandOverTo(curves[0], true, true); curSpace.AppendEntity((Entity)curves[1]); tr.AddNewlyCreatedDBObject(curves[1], true); break; default: break; } curves[0].Dispose(); tr.Commit(); } }
Can't find what you're looking for? Ask the community or share your knowledge.