Recently, while working on a journal that processed existing lines and arcs, I needed to find the start and end points of the given lines and arcs. The line objects have a .StartPoint and .EndPoint property that return a Point3D object. This made writing the code for the line objects easy and I quickly moved on to the arc objects intending to use their .StartPoint and .EndPoint properties; however, this was not to be...
Neither .StartPoint nor .EndPoint properties showed up in the VB editor's 'Intellisense' list for my arc object. Thinking there must be some mistake, I opened the NX8 .NET API reference and looked up the arc class. Unfortunately, arc objects do not offer up their start and end points willingly, as the line objects do. More searching through the documentation failed to turn up a secondary NXOpen .NET function that would return the points of interest (though I'd love to be proved wrong here). So, I started searching in the UF functions and found a few that do the job. Two of the UF functions are shown in the following code along with a function I wrote which uses existing arc properties and a little trigonometry to calculate the start and end points.
UF to the rescue
The first function I found was UF_MODL_ask_curve_props. From the help file:
Returns the point, tangent, unit principal normal, unit binormal, torsion, and radius of curvature on a curve at a given parameter. The input curve parameter, parm, is normalized between 0 and 1.
This function requires 8 parameters to be passed into it:
- input: the tag of the curve of interest
- input: a double value (the curve parameter) representing the length along the curve. In essence, the curve parameter is the percentage distance along the curve, with 0 (0%) representing the curve start point, and 1 (100%) representing the curve end point.
- output: an array of doubles representing the point location. The function will fill this array with the X, Y, and Z values of the point location on the curve at the given parameter.
- output: an array of doubles representing the tangent vector at the point of the given curve parameter.
- output: an array of doubles representing the normal vector of the curve at the given curve parameter point.
- output: an array of doubles representing the binormal vector of the curve at the given curve parameter point.
- output: a double variable representing the torsion of the curve at the given parameter point
- output: a double variable representing the radius of curvature at the given parameter point
In the code below, you will see that I passed in the arc object's .Tag property, the value of 0 (zero) or 1 as the parameter (for the start point and endpoint, respectively), an array to hold the point location information, an array named junk3 (3 times), and a double variable named junk1 (twice). Even though I wasn't interested in most of the function's output, I still had to pass in the required parameters. For what I was doing, I didn't need the tangent, normal, or binormal vectors or the torsion or radius of curvature. Instead of creating 3 extra arrays and 2 extra variables, I cheated a bit by only creating 1 array (junk3) and 1 variable (junk1) and passing them in multiple times to the function. The function got the parameters it required and I got the point information I was interested in, so we were both happy. Well, mostly happy... The UF function was happily offering up a lot of interesting information, but I felt a bit wasteful throwing most of it away. There must be another function to use...
The next UF function I found was UF_CURVE_evaluate_curve, which has 4 required parameters. Here's the overview from the help file:
Returns the point on the curve and the requested derivatives.
This function differs from UF_MODL_evaluate_curve in two ways:
The param and deriv_flag are not pointers, and the param value is specified in the "natural" parameter range of the curve. For example, to evaluate an arc, UF_MODL_evaluate_curve takes a parameter value between 0 and 1, normalized to the parameter range of the arc when created. UF_CURVE_evaluate_curve takes a parameter in radians, in the parameter range given when the arc was created.
This function only requires 4 parameters:
- input: The tag of the curve object, easily obtained by using the .Tag property of my arc object
- input: The curve parameter (or position along the curve of interest). This function uses a 'natural' parameter instead of a 'normalized' parameter, as was used in the UF_MODL_ask_curve_props function. In the case of an arc, a natural parameter means an angle measurement in radians. The arc object conveniently has a .StartAngle and .EndAngle property that represents an angle value measured in radians.
- input: A flag value (integer) that signals what you want the function to return. NXOpen defines some constants to use here:
- UF_CURVE_LOC = return the point
- UF_CURVE_LOC_1STDERV = return the point and 1st derivative
- UF_CURVE_LOC_1STDERV_2NDDERV = return the point, 1st and 2nd derivatives
- output: An array of double values the size of which will depend on what values you have flagged for output
This function looks like it will suit my needs perfectly. I can tell it to only output the point information (the only thing I'm interested in for my current task), and the other input values are easily obtained from the arc object.
Finally, there is an algorithm I came up with while looking for a solution that does not rely on any UF functions. I do not claim that my method is any better or faster than the UF functions, but I'll put it out there as an alternative. Here are the basic steps it takes:
- save the current WCS position and orientation
- move the WCS origin to the center point of the arc
- rotate the WCS to the orientation of the arc
- use the arc's .StartAngle, .EndAngle, and .Radius properties along with some trigonometry to calculate the start and end point locations
- return the WCS to the position before the function was called
At first glance, "rotating the WCS to the orientation of the arc" may sound difficult but the arc object has a .Matrix property which stores the necessary information and can be used directly when orienting the WCS.
On to the Code...
Before you run the code, create/open a file that has several arcs created in it. The journal will prompt you to select an arc, then it will print out the start and end point locations to the listing window. The process will loop until you press 'Cancel'.
'NXJournaling.com 'January 22, 2013 'NX 7.5/8 'Unlike the Line object, the Arc object does not provide startpoint and endpoint properties. 'This journal shows 3 ways to get the arc start/end points. 'Modl.AskCurveProps will work on any curve. It requires a curve parameter as input which ranges from zero to one ' along the length of the curve (zero = start, one = end). It returns other curve properties at that point ' that you may not be interested in (tangent, normal, torsion, etc). 'Curve.EvaluateCurve will also work on any curve. It will return the point and optionally, the 1st and 2nd derivatives ' at that point. The input parameter is 'natural' which, for an arc, means the input parameter must be in radians. ' There is also a Modl.EvaluateCurve function similar to this one, but the parameter is normalized (between 0 and 1). 'The last demonstrated method is my own creation, it does not rely on any UF functions. It works by moving the WCS ' to the centerpoint of the arc and at the same orientation of the arc object. It then uses the arc's startangle ' and endangle properties to calculate the positon of the start/end points and finally maps those points to ' the absolute coordinate space. Option Strict Off Imports System Imports NXOpen Imports NXOpen.UF Module Module1 Dim theSession As Session = Session.GetSession() Dim workPart As Part = theSession.Parts.Work Dim ufs As UFSession = UFSession.GetUFSession Sub Main() Dim lw As ListingWindow = theSession.ListingWindow lw.Open() Dim myArc As Arc Dim myStartPoint As Point3d Dim myEndPoint As Point3d Do Until SelectAnArc("select an arc", myArc) = Selection.Response.Cancel lw.WriteLine("Center point: " & myArc.CenterPoint.ToString) lw.WriteLine("Start angle: " & myArc.StartAngle.ToString) lw.WriteLine("End angle: " & myArc.EndAngle.ToString) lw.WriteLine("") 'use AskCurveProps to find start/end point of arc 'curve parameter is normalized (0 to 1) lw.WriteLine("AskCurveProps") Dim stPt(2) As Double Dim edPt(2) As Double Dim junk3(2) As Double Dim junk1 As Double ufs.Modl.AskCurveProps(myArc.Tag, 0.0, stPt, junk3, junk3, junk3, junk1, junk1) lw.WriteLine("start point: " & stPt(0) & ", " & stPt(1) & ", " & stPt(2)) ufs.Modl.AskCurveProps(myArc.Tag, 1.0, edPt, junk3, junk3, junk3, junk1, junk1) lw.WriteLine("end point: " & edPt(0) & ", " & edPt(1) & ", " & edPt(2)) lw.WriteLine("") 'use EvaluateCurve to find start/end point of arc 'curve parameter is "natural", i.e. the parameter is in radians in the range given when arc was created lw.WriteLine("EvaluateCurve") Dim evalStPt(2) As Double Dim evalEndPt(2) As Double ufs.Curve.EvaluateCurve(myArc.Tag, myArc.StartAngle, UFConstants.UF_CURVE_LOC, evalStPt) lw.WriteLine("start point: " & evalStPt(0) & ", " & evalStPt(1) & ", " & evalStPt(2)) ufs.Curve.EvaluateCurve(myArc.Tag, myArc.EndAngle, UFConstants.UF_CURVE_LOC, evalEndPt) lw.WriteLine("end point: " & evalEndPt(0) & ", " & evalEndPt(1) & ", " & evalEndPt(2)) lw.WriteLine("") 'Calculate start/end points of arc given centerpoint, start angle, end angle, and radius lw.WriteLine("NXJournaling algorithm") myStartPoint = ArcStartPoint(myArc) myEndPoint = ArcEndPoint(myArc) lw.WriteLine("start point: " & myStartPoint.ToString) lw.WriteLine("end point: " & myEndPoint.ToString) lw.WriteLine("") Loop End Sub Function SelectAnArc(ByVal prompt As String, ByRef selObj As NXObject) As Selection.Response Dim theUI As UI = UI.GetUI Dim title As String = "Select an Arc" Dim includeFeatures As Boolean = False Dim keepHighlighted As Boolean = False Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific Dim cursor As Point3d Dim scope As Selection.SelectionScope = Selection.SelectionScope.WorkPart Dim selectionMask_array(0) As Selection.MaskTriple With selectionMask_array(0) .Type = UFConstants.UF_circle_type .Subtype = 0 End With Dim resp As Selection.Response = theUI.SelectionManager.SelectObject(prompt, _ title, scope, selAction, _ includeFeatures, keepHighlighted, selectionMask_array, _ selobj, cursor) If resp = Selection.Response.ObjectSelected OrElse resp = Selection.Response.ObjectSelectedByName Then Return Selection.Response.Ok Else Return Selection.Response.Cancel End If End Function Function Abs2WCS(ByVal inPt As Point3d) As Point3d Dim pt1(2), pt2(2) As Double pt1(0) = inPt.X pt1(1) = inPt.Y pt1(2) = inPt.Z ufs.Csys.MapPoint(UFConstants.UF_CSYS_ROOT_COORDS, pt1, _ UFConstants.UF_CSYS_ROOT_WCS_COORDS, pt2) Abs2WCS.X = pt2(0) Abs2WCS.Y = pt2(1) Abs2WCS.Z = pt2(2) End Function Function WCS2Abs(ByVal inPt As Point3d) As Point3d Dim pt1(2), pt2(2) As Double pt1(0) = inPt.X pt1(1) = inPt.Y pt1(2) = inPt.Z ufs.Csys.MapPoint(UFConstants.UF_CSYS_ROOT_WCS_COORDS, pt1, _ UFConstants.UF_CSYS_ROOT_COORDS, pt2) WCS2Abs.X = pt2(0) WCS2Abs.Y = pt2(1) WCS2Abs.Z = pt2(2) End Function Function ArcStartPoint(ByVal myArc As Arc) As Point3d Dim startWCS As CartesianCoordinateSystem startWCS = workPart.WCS.CoordinateSystem Dim startPT As Point3d = workPart.WCS.Origin Dim startPoint As Point3d workPart.WCS.SetOriginAndMatrix(myArc.CenterPoint, myArc.Matrix.Element) 'start point startPoint.X = myArc.Radius * Math.Cos(myArc.StartAngle) startPoint.Y = myArc.Radius * Math.Sin(myArc.StartAngle) startPoint.Z = 0 'convert point work coordinates to ACS Return WCS2Abs(startPoint) workPart.WCS.SetOriginAndMatrix(startPT, startWCS.Orientation.Element) End Function Function ArcEndPoint(ByVal myArc As Arc) As Point3d Dim startWCS As CartesianCoordinateSystem startWCS = workPart.WCS.CoordinateSystem Dim endPoint As Point3d workPart.WCS.SetOriginAndMatrix(myArc.CenterPoint, myArc.Matrix.Element) 'end point endPoint.X = myArc.Radius * Math.Cos(myArc.EndAngle) endPoint.Y = myArc.Radius * Math.Sin(myArc.EndAngle) endPoint.Z = 0 'convert point work coordinates to ACS Return WCS2Abs(endPoint) workPart.WCS.SetOriginAndMatrix(New Point3d(0, 0, 0), startWCS.Orientation.Element) End Function Public Function GetUnloadOption(ByVal dummy As String) As Integer 'Unloads the image when the NX session terminates GetUnloadOption = NXOpen.Session.LibraryUnloadOption.AtTermination End Function End Module