Scene: a non-descript grey fabric covered box that currently contains a workstation running NX and 2 coworkers
Coworker: Hey, I need to measure the length of this tube I have...
NXJournaling: No problem, here's a journal that lets you select a tube and adds up the lengths of the defining sections
Coworker: actually, it is from an imported part; there are no defining objects in the file...
NXJournaling: Ok, assuming it is made of straights and bends, we can extract lines from the cylindrical faces then...
Coworker: Well, it doesn't have straight sections or simple bends so much... it seems to be made of B-surfaces
NXJournaling: oh, I see... well... hmmm
NXJournaling: Let me introduce you to Frank Swinkels. He is a regular contributor at the GTAC languages forum and also at the eng-tips Siemens NX forum and he likes to write and share the type of code that can get you out of situations like this.
He has given us permission to reproduce some code he originally posted in the GTAC forum in answer to the request for extracting the centerline of an unparameterized free form tube. The code works the following way:
- The user is prompted to select a B-surface face.
- Collect the B-surface data, make sure the surface is closed in the U direction. Use the V direction surface data to help generate the tube centerline.
- At each point of interest along the length of the tube, generate equally spaced points around the circumference (U direction) of the tube. The average of these points determines the center point of the tube cross section.
- Construct a studio spline through these points which will closely approximate the centerline of the tube.
Notes
This journal will only work with NX 8 or above.
I encourage you to step through the code to see what is going on, I can almost guarantee that you will learn something.
The code demonstrates the use of the AskFaceProps function and a couple of vector functions that may come in handy. Also, there is a subroutine for creating a through points studio spline. Pass in an array of points and the desired degree and the spline will be created. That's a good piece of reusable code to have on hand...
The Code
Download the code and a sample part.
'Author: Frank Swinkels 'for use only with NX 8.5 or above 'Create approximate centerline of B-surface "tube" 'user will be prompted to select B-surface, a 'through points' studio spline will be created along the centerline ' modified June 19, 2014 to include creating the bsurface and allow for faces with other then 0 to 1 parameters ' (typically cylinders or extended cylinders) ' ' modified June 24 to change for selecting apply or OK ' ' NXJournaling.com ' Report length of selected tube. Option Strict Off Imports System Imports NXOpen Imports NXOpen.UF Imports NXOpenUI Imports NXOpen.Features Imports System.IO Module CableCentreline Dim s As Session = Session.GetSession() Dim ufs As UFSession = UFSession.GetUFSession() Dim workPart As Part = s.Parts.Work Dim lw As ListingWindow = s.ListingWindow Sub Main() Dim no_pts As Integer = Nothing Dim pos1 As Integer = Nothing Dim junk3(2) As Double Dim junk2(1) As Double Dim periodic1 As Boolean = False Dim theFace As Face = Nothing Dim totalLength As Double = 0 Dim response1 As Selection.Response = Selection.Response.Cancel Dim type1 As Integer = Nothing Dim subtype1 As Integer = Nothing Dim bsurface1 As UFModl.Bsurface = Nothing Dim extractedfeat1 As Feature = Nothing Dim extractedbodyfeat1 As BodyFeature = Nothing Dim extractedbody1() As Body = Nothing Dim faces() As Face Dim testface As Face lw.Open() While select_a_face("Select a tube extracted face or dumb solid face", theFace) = Selection.Response.Ok 'response1 = select_a_face("Select a tube extracted face or dumb solid face", theFace) 'If response1 = Selection.Response.Cancel Or response1 = Selection.Response.Back Then GoTo end1 ' check that it is a bsurface Try ufs.Modl.AskBsurf(theFace.Tag, bsurface1) testface = theFace Catch ex As Exception createExtractedBSurface(theFace, extractedfeat1) extractedbodyfeat1 = DirectCast(extractedfeat1, BodyFeature) extractedbody1 = extractedbodyfeat1.GetBodies faces = extractedbody1(0).GetFaces ufs.Modl.AskBsurf(faces(0).Tag, bsurface1) testface = faces(0) End Try Dim noPolesV As Integer = bsurface1.num_poles_v Dim noPolesU As Integer = bsurface1.num_poles_u Dim poles1(,) As Double = bsurface1.poles Dim knotsU() As Double = bsurface1.knots_u Dim knotsV() As Double = bsurface1.knots_v Dim orderU As Integer = bsurface1.order_u Dim orderV As Integer = bsurface1.order_v Dim ptno As Integer = Nothing Dim params(1) As Double Dim pnt0(2) As Double Dim pnt1(2) As Double Dim pnt2(2) As Double Dim pnt3(2) As Double Dim vec0(2) As Double Dim vec1(2) As Double Dim vec2(2) As Double Dim vec3(2) As Double Dim rads(1) As Double Dim tolerance1 As Double = 0.001 Dim magnitude1 As Double = Nothing Dim degree3 As Integer = orderV - 1 Dim pointtag As Tag = Tag.Null Dim ArrayOfPoints(-1) As Point Dim uparm() As Double = {0.0, 0.25, 0.5, 0.75} Dim delta1 As Double Dim sum1 As Double = Nothing Dim distance1 As Double = Nothing Dim cnt1 As Integer = 0 Dim tempcpt(2) As Double Dim coordinates1 As Point3d Dim temptag As Tag = Tag.Null ' tube bsurf u around tube, v along the tube Dim vparm(noPolesV - 1) As Double vparm(0) = 0.0 vparm(noPolesV - 1) = 1.0 Dim lengths(noPolesV - 2) As Double sum1 = 0.0 cnt1 = 0 For i As Integer = noPolesU To noPolesU * noPolesV - 1 Step noPolesU pnt1(0) = poles1(i, 0) pnt1(1) = poles1(i, 1) pnt1(2) = poles1(i, 2) pnt2(0) = poles1(i - noPolesU, 0) pnt2(1) = poles1(i - noPolesU, 1) pnt2(2) = poles1(i - noPolesU, 2) ufs.Vec3.Distance(pnt1, pnt2, distance1) lengths(cnt1) = distance1 cnt1 += 1 sum1 += distance1 Next delta1 = 0.0 For i As Integer = 0 To noPolesV - 2 delta1 += lengths(i) vparm(i + 1) = delta1 / sum1 Next Dim uvminmax(3) As Double ufs.Modl.AskFaceUvMinmax(testface.Tag, uvminmax) Dim total2 As Double = uvminmax(3) - uvminmax(2) Dim delta2 As Double = total2 / (noPolesV - 1) Dim zero1 As Double = uvminmax(2) For i As Integer = 0 To noPolesV - 1 If i = 0 Then vparm(i) = zero1 Else vparm(i) = vparm(i - 1) + delta2 End If Next For i As Integer = 0 To noPolesV - 1 params(0) = uparm(0) params(1) = vparm(i) ufs.Modl.AskFaceProps(testface.Tag, params, pnt0, junk3, vec0, junk3, junk3, junk3, rads) ufs.Vec3.Unitize(vec0, tolerance1, magnitude1, vec0) params(0) = uparm(1) ufs.Modl.AskFaceProps(testface.Tag, params, pnt1, junk3, vec1, junk3, junk3, junk3, rads) params(0) = uparm(2) ufs.Modl.AskFaceProps(testface.Tag, params, pnt2, junk3, vec2, junk3, junk3, junk3, rads) params(0) = uparm(3) ufs.Modl.AskFaceProps(testface.Tag, params, pnt3, junk3, vec3, junk3, junk3, junk3, rads) ufs.Vec3.Add(pnt0, pnt1, tempcpt) ufs.Vec3.Add(pnt2, tempcpt, tempcpt) ufs.Vec3.Add(pnt3, tempcpt, tempcpt) tempcpt(0) /= 4.0 tempcpt(1) /= 4.0 tempcpt(2) /= 4.0 coordinates1 = New Point3d(tempcpt(0), tempcpt(1), tempcpt(2)) ReDim Preserve ArrayOfPoints(i) ArrayOfPoints(i) = workPart.Points.CreatePoint(coordinates1) Next periodic1 = False Dim myStudioSpline As Features.StudioSpline myStudioSpline = CreateStudioSplineThruPoints(ArrayOfPoints, degree3) Dim tubeLength As Double = 0 For Each tempCurve As Curve In myStudioSpline.GetEntities tubeLength += tempCurve.GetLength Next totalLength += tubeLength lw.WriteLine("section length: " & tubeLength.ToString) lw.WriteLine("total length: " & totalLength.ToString) lw.WriteLine("") End While end1: End Sub Public Sub createExtractedBSurface(ByVal face1 As Face, ByRef extractedfeat1 As Feature) Dim nullFeatures_Feature As Features.Feature = Nothing Dim extractFaceBuilder1 As Features.ExtractFaceBuilder extractFaceBuilder1 = workPart.Features.CreateExtractFaceBuilder(nullFeatures_Feature) extractFaceBuilder1.ParentPart = Features.ExtractFaceBuilder.ParentPartType.WorkPart extractFaceBuilder1.Associative = True extractFaceBuilder1.FixAtCurrentTimestamp = True extractFaceBuilder1.HideOriginal = False extractFaceBuilder1.Type = Features.ExtractFaceBuilder.ExtractType.Face extractFaceBuilder1.InheritDisplayProperties = False extractFaceBuilder1.SurfaceType = Features.ExtractFaceBuilder.FaceSurfaceType.PolynomialCubic Dim added1 As Boolean added1 = extractFaceBuilder1.ObjectToExtract.Add(face1) extractedfeat1 = extractFaceBuilder1.Commit End Sub Function select_a_face(ByRef prompt As String, ByRef face1 As Face) As Selection.Response Dim mask(0) As Selection.MaskTriple With mask(0) .Type = UFConstants.UF_solid_type .Subtype = 0 .SolidBodySubtype = UFConstants.UF_UI_SEL_FEATURE_ANY_FACE End With Dim cursor As Point3d = Nothing Dim response1 As Selection.Response = Selection.Response.Cancel select_a_face = Nothing response1 = UI.GetUI.SelectionManager.SelectTaggedObject(prompt, "Select the Face", _ Selection.SelectionScope.AnyInAssembly, _ Selection.SelectionAction.ClearAndEnableSpecific, False, _ False, mask, face1, cursor) If response1 = Selection.Response.ObjectSelected Or _ response1 = Selection.Response.ObjectSelectedByName Then Return Selection.Response.Ok ElseIf response1 = Selection.Response.Back Then Return Selection.Response.Back Else Return Selection.Response.Cancel End If End Function Public Function CreateStudioSplineThruPoints(ByRef points() As Point, ByVal degree3 As Integer) As Features.StudioSpline Dim markId9 As Session.UndoMarkId markId9 = s.SetUndoMark(Session.MarkVisibility.Visible, "Studio Spline Thru Points") Dim Pcount As Integer = points.Length - 1 Dim nullFeatures_StudioSpline As Features.StudioSpline = Nothing Dim studioSplineBuilderex1 As Features.StudioSplineBuilderEx studioSplineBuilderex1 = workPart.Features.CreateStudioSplineBuilderEx(nullFeatures_StudioSpline) studioSplineBuilderex1.OrientExpress.ReferenceOption = GeometricUtilities.OrientXpressBuilder.Reference.ProgramDefined studioSplineBuilderex1.Degree = degree3 studioSplineBuilderex1.OrientExpress.AxisOption = GeometricUtilities.OrientXpressBuilder.Axis.Passive studioSplineBuilderex1.OrientExpress.PlaneOption = GeometricUtilities.OrientXpressBuilder.Plane.Passive studioSplineBuilderex1.MatchKnotsType = Features.StudioSplineBuilderEx.MatchKnotsTypes.None Dim knots1(-1) As Double studioSplineBuilderex1.SetKnots(knots1) Dim parameters1(-1) As Double studioSplineBuilderex1.SetParameters(parameters1) Dim nullDirection As Direction = Nothing Dim nullScalar As Scalar = Nothing Dim nullOffset As Offset = Nothing Dim geometricConstraintData(Pcount) As Features.GeometricConstraintData For ii As Integer = 0 To Pcount geometricConstraintData(ii) = studioSplineBuilderex1.ConstraintManager.CreateGeometricConstraintData() geometricConstraintData(ii).Point = points(ii) geometricConstraintData(ii).AutomaticConstraintDirection = Features.GeometricConstraintData.ParameterDirection.Iso geometricConstraintData(ii).AutomaticConstraintType = Features.GeometricConstraintData.AutoConstraintType.Tangent geometricConstraintData(ii).TangentDirection = nullDirection geometricConstraintData(ii).TangentMagnitude = nullScalar geometricConstraintData(ii).Curvature = nullOffset geometricConstraintData(ii).CurvatureDerivative = nullOffset geometricConstraintData(ii).HasSymmetricModelingConstraint = False Next ii studioSplineBuilderex1.ConstraintManager.SetContents(geometricConstraintData) Dim feature1 As Features.StudioSpline feature1 = studioSplineBuilderex1.CommitFeature() studioSplineBuilderex1.Destroy() Return feature1 End Function Public Function GetUnloadOption(ByVal dummy As String) As Integer 'Unloads the image when the NX session terminates GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately End Function End Module

Comments
NX6
Would it be possible to get this code to work in NX6
Paul Fillion
re: run in NX 6
I don't have NX 6 installed to do any testing.
What errors you do get when you try to run it in NX 6?
error messages
Okay this is what the Journal Compile Errors Says
Paul Fillion
re: NX 6 error messages
Fixing the SelectTaggedObject error would be easy, but some of the code that uses the StudioSplineBuilderEx (NX 8.0 object) doesn't have a direct replacement with the older StudioSplineBuilder (pre NX 8.0) object. I'm pretty sure it could be rewritten to work with NX 6, but offhand, I don't know how much trouble it would be.
Extract Centerline from Tube
Does anyone know Frank Swinkels? Maybe possible he has an older version.
Paul Fillion
re: tube centerline journal
I contacted Frank, he has very kindly responded with code that should work in NX 6.
Big thanks to Frank!
You know you guys are
You know you guys are FREAKING AWESOME! Yeah Frank very nice Thank You very much
Paul Fillion
Centerline of b-surface tube
Here's a much easier approach. Basically, you just construct two iso-curves running along opposite sides of the tube, connect them with a ruled surface, and get the mid-curve of this ruled surface.
Option Infer On Imports Snap, Snap.Create, Snap.UI, Snap.NX.ObjectTypes Public Class MyProgram Public Shared Sub Main() ' Create a selection dialog and set filter Dim dialog = Selection.SelectObject("Select a b-surface tube face") dialog.SetFaceFilter(SubType.FaceBsurface) ' Display the dialog and get the face Dim result = dialog.Show() Dim bsurf As NX.Face.Bsurface = Nothing If result.Response <> Response.Cancel And result.Response <> Response.Back Then bsurf = result.Object End If ' Create two iso-curves running along opposite sides of the tube Dim side1 As NX.Spline = bsurf.IsoCurveU(0.25)(0) Dim side2 As NX.Spline = bsurf.IsoCurveU(0.75)(0) ' Create a ruled surface between the two iso-curves Dim crossSurf As NX.Face.Bsurface = Ruled(side1, side2).Faces(0) ' Construct the center curve of the ruled surface Dim centerline As NX.Spline = crossSurf.IsoCurveV(0.5)(0) End Sub End ClassThis uses SNAP functions, but you can do roughly the same thing with older functions: use NXOpen.UF.UFModl.CreateIsocurve to create the iso-curves and NXOpen.UF.UFModl.CreateRuled to create the ruled surface.
B curves
Okay thanks I will give it a shot
Paul Fillion
centerline of B surface
Okay I gave it the Boy Scout try.
Theses are the codes I got
Paul Fillion