We discussed and demonstrated about a few practices (in both .NET and ObjectARX) previously regarding getting the whole length of Curve derivative entities in AutoCAD such as Line, XLine, Ray, Polyline (lightweight, 2D or 3D), Spline, Arc, Circle, Ellipse, and even Leader.
The XLine and Ray entities throw out the eNotApplicable exceptions when their start/end parameters being accessed, though it makes more sense for our help methods to return the double.PositiveInfinity in this case since the C# and many other .NET languages have already supported this notation, as Jeff H pointed out.
In this post, let us look at a real and good use case about using the Curve.GetDistanceAtParameter and the Curve.GetParameterAtPoint APIs. Here is the help method and the test command:
public static double GetLength(Curve ent, Point3d pt1, Point3d pt2)
{
if (ent == null)
throw new ArgumentNullException("The passed in Curve is null.");
double dist1 = ent.GetDistanceAtParameter(ent.GetParameterAtPoint(pt1));
double dist2 = ent.GetDistanceAtParameter(ent.GetParameterAtPoint(pt2));
return Math.Abs(dist1 - dist2);
}
[CommandMethod("GetLengthGoodUseCase_Test")]
public static void GetLengthGoodUseCase_Test_Method()
{
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Database db = HostApplicationServices.WorkingDatabase;
try
{
PromptEntityResult selRes = ed.GetEntity("\nPick a Curve entity");
PromptPointResult prPntRes1 = ed.GetPoint("\nPick a point on the Curve");
PromptPointResult prPntRes2 = ed.GetPoint("\nPick another point on the same Curve");
if (selRes.Status == PromptStatus.OK && prPntRes1.Status == PromptStatus.OK && prPntRes2.Status == PromptStatus.OK)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
Entity ent = (Entity)tr.GetObject(selRes.ObjectId, OpenMode.ForRead);
ed.WriteMessage("\nThe length between the point {0} and the point {1}\n on the {2} is: {3}",
prPntRes1.Value.ToString(), prPntRes2.Value.ToString(),
ent.GetType().Name,
GetLength(ent as Curve, prPntRes1.Value, prPntRes2.Value));
tr.Commit();
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.ToString());
}
}
Some different kinds of Curve entities need to be prepared for making the code/command test accountable. Here is a simple but good example (people can figure out what Curve each entity is from its appearance, so more words seem not necessary).
Here is the output of the various tests.
Command: GetLengthGoodUseCase_Test
Pick a Curve entity:
Pick a point on the Curve: <Osnap on>
Pick another point on the same Curve:
The length between the point (30.8530778501108,7.22007922071122,0) and the
point (32.3088973373407,17.5444825756132,0)
on the Xline is: 1.89090823635486
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (21.3657434791291,11.3309091817859,0) and the
point (22.2818774882339,3.56641283613888,0)
on the Spline is: 11.0241037365354
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (28.1674057197096,1.91990869418836,0) and the
point (26.3172667138677,21.5826951912377,0)
on the Ray is: 19.7496376465363
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (41.2228701663762,6.2905294898654,0) and the point
(46.8133504808149,13.9264108159715,0)
on the Ellipse is: 9.61589485190486
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (5.81705107543273,5.44820775193322,0) and the
point (9.23042982019822,13.4618579977536,0)
on the Line is: 8.7103240305716
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (14.3157494310502,16.8578765122213,0) and the
point (16.858244453406,5.71272300065424,0)
on the Polyline is: 15.590265048356
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (34.8208270157394,13.1936488573327,0) and the
point (37.7028528302892,16.8008943039825,0)
on the Arc is: 4.78587733222559
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve:
The length between the point (37.0073684879513,8.90341332302878,0) and the
point (50.2481164129678,2.77380133116883,0)
on the Circle is: 18.1609824840572
Command: GETLENGTHGOODUSECASE_TEST
Pick a Curve entity:
Pick a point on the Curve:
Pick another point on the same Curve: Autodesk.AutoCAD.Runtime.Exception:
eInvalidInput
at Autodesk.AutoCAD.DatabaseServices.Curve.GetParameterAtPoint(Point3d point)
at AcadNetAddinWizard_Namespace.TestCommands.GetLength(Curve ent, Point3d
pt1, Point3d pt2) in ...
As can be seen, all other Curve entities behave fine except for the last (Leader). Its GetParameterAtPoint() call does not accept the second point input though it is also on the Curve/Leader. The reason is not clear at this moment. It is likely caused by some API design or implementation issues because our GetLength() help method that was introduced previously could get the whole length out of the Leader successfully. The real point is: if the Leader.StartParam and the Leader.EndParam make sense for the Leader entity, why Leader.GetParameterAtPoint(Point3d pointOnCurve) NOT?
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) provides project wizards in C#, VB.NET and CLI/Managed C++, and various item wizards such as Event Handlers, Command/LispFunction Definers, and Entity/Draw Jiggers in both C# and VB.NET, to help program AutoCAD addins.
Hey Spiderinnet1,
Sorry to keep bothering you but, I do not get error with a leader, and only difference I could tell was checking if point was on the curve.
Speaking of which,
I have not tested the IsPointOn method for performance and accuracy, but where do you think that logic belongs? Should it be placed in GetLength method or maybe is that the responsiblity of caller to make sure the point is on the curve?
using System;
using Autodesk.AutoCAD.Geometry;
namespace Autodesk.AutoCAD.DatabaseServices
{
public static class CurveExtensions
{
//Slighty modified from original author Tony Tanzillo
public static double GetLength(this Curve curve)
{
if (curve == null)
{
throw new ArgumentNullException("Curve was Null");
}
if (curve is Xline || curve is Ray)
{
return Double.PositiveInfinity;
}
return Math.Abs(curve.GetDistanceAtParameter(curve.EndParam) - curve.GetDistanceAtParameter(curve.StartParam));
}
public static double GetLength(this Curve curve, Point3d startPoint, Point3d endPoint)
{
if (curve == null)
{
throw new ArgumentNullException("Curve was Null");
}
if (!curve.IsOnPoint(startPoint))
{
throw new ArgumentException("First Point is not on Curve");
}
if (!curve.IsOnPoint(endPoint))
{
throw new ArgumentException("Second Point is not on Curve");
}
double startDist = curve.GetDistanceAtParameter(curve.GetParameterAtPoint(startPoint));
double endDist = curve.GetDistanceAtParameter(curve.GetParameterAtPoint(endPoint));
return Math.Abs(endDist - startDist);
}
/// Author: Kean Walmsley
/// Source: http://through-the-interface.typepad.com/through_the_interface/2012/01/testing-whether-a-point-is-on-any-autocad-curve-using-net.html
///
///
///
///
public static bool IsOnPoint(this Curve curve, Point3d pnt)
{
Point3d p = curve.GetClosestPointTo(pnt, false);
return (p - pnt).Length <= Tolerance.Global.EqualPoint;
}
}
}
Posted by: JeffH | 10/18/2012 at 05:32 PM
Jeff, sorry for not answering your questions earlier.
It's nothing wrong to check wether the points are on the curve inside the help method itself, but I would personaly perfer to do so outside of the method. There are a few reasons, as mentioned in some recent posts.
If the points are picked by users through the Editor.GetPoint calls, the returned points are in UCS, so we need to transform them from UCS to WCS as well. We'd better provide users a way to pick points on the curve using the Nearest or other object snapping modes for example at the first hand. If we really want to check if the point is on the curve or not by ourself, we'd better use the ClosestPoint on the curve instead of the original point (if it meets the IsOn creteria) to feed the GetParameterAtPoint call since the formmer may be less tolerant than the latter.
In addition, the Point3d has the methods DistanceTo() and IsEqualTo() which uses the internal precision of AutoCAD, so we'd better forget about the equation of calculating the distance and comparing it to the tolerance explicitly.
Posted by: Spiderinnet1 | 10/24/2012 at 12:02 AM
Thanks,
Once again I enjoy and learn from reading your blog.
Thanks again!!
Posted by: JeffH | 10/24/2012 at 12:13 PM