Arc start point and end point

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.

Home-grown algorithm

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'.

'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
        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)
            'use AskCurveProps to find start/end point of arc
            'curve parameter is normalized (0 to 1)
            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))
            '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
            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))
	    '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)
    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
            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


Please Help.

The following code gives me an error
ufs.Modl.UF_ROUTE_ask_segment_wires(routingSplineTag , numWires , wires)
Journal Compile Errors: 'UF_ROUTE_ask_segment_wires' is not a member of 'NXOPEN.UF.UFModl'

So i tried something diferent, but still get errors.
ufs.route.UF_ROUTE_ask_segment_wires(routingSplineTag , numWires , wires)
Journal Compile Errors: 'UF_ROUTE_ask_segment_wires' is not a member of 'NXOPEN.UF.UFRoute'

Carlo Tony Daristotile

Make sure your code imports NXOpen.UF and the ufsession is created.

Option Strict Off
Imports System
Imports NXOpen
Imports NXOpen.UF
    Dim theUfSession As UFSession = UFSession.GetUFSession

The only part corrected then was "AskSegmentWires"
I see what I did wrong, I was using "Wrapper method for UF_ROUTE_ask_segment_wires"

Carlo Tony Daristotile

hello I work with UG NX routing electrical.

What I would like to do is get the information on the wires for any selected routing.splinesegment.

This function "AskSegmentWires" helped me get the number of wires.
But I dont know how to use the wire tags array to get information on each wire, such as wire gauge, wire diameter, wire type, from conn and pin, to conn and pin, etc..

I cannot find the "ask" functions on this information, and the wire class doesnt have this information.

Any ideas?

Carlo Tony Daristotile

I'm not familiar with the routing application, but looking through the API reference, I found some functions that may help you:

Searching on the term "UF_ROUTE_ask*" will turn up more that may be of interest.

the API functions you have described are very helpful to get the xyz coordinates of
a point located at a particular percentage position on the curve. My question, can I do the opposite?
Is there a function to get the percentage of a point on a curve?

I think the function you are looking for is "UF_EVAL_evaluate_closest_point". You will need to do some work to initialize the "evaluator", after which this function will return the closest point (and corresponding parameter) on the given curve to the given point.

I've tried to use these API function for getting the curve parameter of a given point on the curve.
Acc. the API reference the resulting parameter is not normalized. What does this mean? When I use the
result parameter to create a new point on the curve, there is a difference between the first point
and the newly created point. How can I normalize the result?

What do you plan on doing with the curve parameter? If you only want to create a new point at that location, I suggest using the CreatePoint method that allows you to specify the close point and the offset from that point.

Can you post the relevant section of code you have so far?

Yes, I want to create a new point coincident with the first point, this new point should be parametric moveable along the curve with it's curve parameter. I do not have a code until yet, but my idea was to do it like this:

> Create an Intersection point between a plane (z=0) and the curve
> Using the curve parameter of the intersection point to create an associative point along the curve.

Problem is, if I use the curve parameter of the intersection point, the new created point is not coincident with the intersection point.

Perhaps the function:

will work better here. This one returns a normalized parameter value.