Getting Thread Information in Inventor and Apprentice

If you need to write a program that accesses threads in a model, the first thought it to get the thread information from the feature that created the thread.  Threads can be created by adding a tapped hole or using the thread feature.  This approach works but requires you to write two paths of code to handle the two different types of features.  However, there are also cases where this approach doesn’t work.

Problems Getting Thread Information

There are several construction techniques where the original features that created the thread are not accessible.

Derived Parts – Whenever you derive a part, the derivation is just the body and you lose all of the feature information.

iParts – When you use iParts and create members, each member is a derived part and doesn’t have any of the original feature information.

iFeatures –  After placing an iFeature that contains one or more tapped holes or threads, all you have is an iFeature and not the features that created the threads.

Apprentice – Another case is when you use Apprentice to access the part, whether it’s the original part that contains the features or a derived part.  Apprentice doesn’t support access to any feature information, even when there is feature information in the part.

There’s some functionality that has existed in the API for a long time and is documented in the help, but I don’t think has ever been pointed out and explained in much detail.  This functionality provides access to thread information in all the above cases and it also useful in the case where you do have access to the feature information because your code can be simpler by not having to support querying two different types of features.

Alternate Approach to Getting Thread Information

The alternative approach for getting thread information utilizes a capability exposed through the Face object, which supports the ThreadInfos property.  In most cases this property will return Nothing, indicating that there isn’t any associated thread information.  However, for cylindrical or conical faces that have threads, this property will return an ObjectCollection that contains one or more ThreadInfo objects.  The ThreadInfo object provides all of the information needed to fully describe the thread.

The picture above is an example model that illustrates various types of threads and ways to create threads.  These are all created either by creating a tapped hole or a thread feature.  Both of these methods support creating standard and tapered threads.  One capability when creating tapped holes is that you can use the “From Sketch” option to create multiple holes (and threads) within a single hole feature; one at each sketch point.  The example above illustrates a couple of other things where there are two thread features on a single face, and another has a tapped hole and a thread feature adding threads to the other end of the hole.  These are easily handled because the collection returned by the ThreadInfos property will contain two ThreadInfo objects.

The VBA code below iterates over the faces in the model, looking for any cylindrical or conical faces that have threads associated with them.  It then dumps out information about the threads.  With one exception (described in more detail below), this code works the same if you run it in the original part that contains all of the features, or run it in a derivation of that part.  Although this is quite a bit of code, 90% of it writing out the thread information, and the code to get access to the thread information is all in the first For loop.

' Example that illustrates getting thread information from faces.
Public Sub GetThreads()
    Dim partDoc As PartDocument
    Set partDoc = ThisApplication.ActiveDocument
    ' Write the results to a file.
    Dim threadFile As Integer
    threadFile = FreeFile
    Open "C:\Temp\ThreadInfo.txt" For Output As #threadFile
    ' Iterate through all of the faces in the first body of the part.
    Dim fc As Face
    For Each fc In partDoc.ComponentDefinition.SurfaceBodies.Item(1).Faces
        ' Look for cylindrical or conical faces.
        If fc.SurfaceType = kCylinderSurface Or fc.SurfaceType = kConeSurface Then
            If Not fc.ThreadInfos Is Nothing Then
                ' Check to see if there are any threads on this face.
                If fc.ThreadInfos.Count > 0 Then
                    ' Get the information for this thread.
                    Dim thread As ThreadInfo
                    For Each thread In fc.ThreadInfos
                        Print #threadFile, "Thread"
                        Call WriteFaceInfo(threadFile, fc)
                        Call WriteThreadInfo(threadFile, fc, thread)
                End If
            End If
        End If
    MsgBox "Thread result written to ""C:\Temp\ThreadInfo.txt"""
End Sub

' Write out geometric information about the cylindrical or conical face.
Private Sub WriteFaceInfo(threadFile As Integer, threadFace As Face)
    If threadFace.SurfaceType = kCylinderSurface Then
        Dim cyl As Cylinder
        Set cyl = threadFace.Geometry
        Print #threadFile, "   Cylinder Geometry"
        Print #threadFile, "      Cylinder Diameter: " & cyl.Radius * 2
        Print #threadFile, "      Cylinder Position: " & GetPointOrVector(cyl.basePoint)
        Print #threadFile, "      Cylinder Direction: " & GetPointOrVector(cyl.AxisVector)
    ElseIf threadFace.SurfaceType = kConeSurface Then
        Dim cn As Cone
        Set cn = threadFace.Geometry
        Print #threadFile, "   Cone Geometry"
        Print #threadFile, "      Cone Diameter: " & cn.Radius * 2
        Print #threadFile, "      Cone Position: " & GetPointOrVector(cn.basePoint)
        Print #threadFile, "      Cone Direction: " & GetPointOrVector(cn.AxisVector)
        Print #threadFile, "      Cone Angle: " & Format(cn.HalfAngle * (180 / 3.14159265358979), "0.000")
    End If
End Sub

' Write out all of the information provided by a ThreadInfo object.
Private Sub WriteThreadInfo(fc As Face, thread As ThreadInfo)
    ' Write out the common, generic thread information.
    Print #threadFile, "   General Thread Info"
    Print #threadFile, "      Designation: " & thread.ThreadDesignation
    Print #threadFile, "      Custom Designation: " & thread.CustomThreadDesignation
    Print #threadFile, "      Full thread depth: " & thread.FullThreadDepth
    Print #threadFile, "      Internal: " & thread.Internal
    Print #threadFile, "      Metric: " & thread.Metric
    Print #threadFile, "      RightHanded: " & thread.RightHanded
    Print #threadFile, "      Thread Type: " & thread.ThreadType
    Print #threadFile, "      Thread Type Identifier: " & thread.ThreadTypeIdentifier
    Print #threadFile, "      Direction: " & GetPointOrVector(thread.ThreadDirection.AsUnitVector)
    If Not thread.FullThreadDepth Then
        Print #threadFile, "      Depth: " & Format(thread.ThreadDirection.Length, "0.00000")
    End If
    Print #threadFile, "      Locations:"
    Dim basePoint As Point
    For Each basePoint In thread.ThreadBasePoints
        Print #threadFile, "         Position: " & GetPointOrVector(basePoint)

    ' If a tapped hole is created using a sketch so that multiples holes are placed as
    ' a single hole feature, the ThreadBasePoints property will return the location of
    ' all of the holes.  However, there's a problem because each of the physical holes
    ' created by the feature will return this set of points. This code is used to find
    ' the coordinate that is used for this specific physical hole.
    If thread.ThreadBasePoints.Count > 1 Then
        ' Create a transient line along the axis of the cylinder or cone.
        Dim fcLine As Line
        Set fcLine = ThisApplication.TransientGeometry.CreateLine(fc.Geometry.basePoint, fc.Geometry.AxisVector.AsVector)
        ' Check to see which hole base point lies on the axis.
        Dim foundBasePoint As Point
        Set foundBasePoint = Nothing
        For Each basePoint In thread.ThreadBasePoints
            Dim dist As Double
            dist = ThisApplication.MeasureTools.GetMinimumDistance(basePoint, fcLine)
            If dist < 0.000001 Then
                Set foundBasePoint = basePoint
                Exit For
            End If
        Print #threadFile, "         Position Used: " & GetPointOrVector(foundBasePoint)
    End If
    If TypeOf thread Is StandardThreadInfo Then
        ' Print out information for standard threads.
        Print #threadFile, "   Standard Thread Info"
        Dim standard As StandardThreadInfo
        Set standard = thread
        Print #threadFile, "      Class: " & standard.Class
        Print #threadFile, "      Max Major Dia.: " & Format(standard.MajorDiameterMax, "0.00000")
        Print #threadFile, "      Min Major Dia.: " & Format(standard.MajorDiameterMin, "0.00000")
        Print #threadFile, "      Max Minor Dia.: " & Format(standard.MinorDiameterMax, "0.00000")
        Print #threadFile, "      Min Minor Dia.: " & Format(standard.MinorDiameterMin, "0.00000")
        Print #threadFile, "      Nominal Size: " & Format(standard.NominalSize, "0.00000")
        Print #threadFile, "      Pitch: " & Format(standard.Pitch, "0.00000")
        Print #threadFile, "      Max Pitch Dia.: " & Format(standard.PitchDiameterMax, "0.00000")
        Print #threadFile, "      Min Pitch Dia.: " & Format(standard.PitchDiameterMin, "0.00000")
        Print #threadFile, "      Tap Drill Dia.: " & Format(standard.TapDrillDiameter, "0.00000")
    ElseIf TypeOf thread Is TaperedThreadInfo Then
        ' Print out information for tapered threads.
        Print #threadFile, "   Tapered Thread Info"
        Dim tapered As TaperedThreadInfo
        Set tapered = thread
        Print #threadFile, "      Basic Minor Dia.: " & Format(tapered.BasicMinorDiameter, "0.00000")
        Print #threadFile, "      Effective Dia.: " & Format(tapered.EffectiveDiameter, "0.00000")
        Print #threadFile, "      Effective Length: " & Format(tapered.EffectiveLength, "0.00000")
        Print #threadFile, "      Engagement Dia.: " & Format(tapered.EngagementDiameter, "0.00000")
        Print #threadFile, "      Engagement Length: " & Format(tapered.EngagementLength, "0.00000")
        Print #threadFile, "      External Pitch Dia.: " & Format(tapered.ExternalPitchDiameter, "0.00000")
        Print #threadFile, "      External Nominal Dia.: " & Format(tapered.NominalExternalDiameter, "0.00000")
        Print #threadFile, "      External Nomminal Length: " & Format(tapered.NominalExternalLength, "0.00000")
        Print #threadFile, "      Outside Pipe Dia.: " & Format(tapered.OutsidePipeDiameter, "0.00000")
        Print #threadFile, "      Overall External Length: " & Format(tapered.OverallExternalLength, "0.00000")
        Print #threadFile, "      Tap Drill Dia.: " & Format(tapered.TapDrillDiameter, "0.00000")
        Print #threadFile, "      Thread Height: " & Format(tapered.ThreadHeight, "0.00000")
        Print #threadFile, "      Threads Per Inch: " & Format(tapered.ThreadsPerInch, "0.00000")
        Print #threadFile, "      Vanish Thread: " & Format(tapered.VanishThread, "0.00000")
        Print #threadFile, "      Wrench Makeup Dia.: " & Format(tapered.WrenchMakeupDiameter, "0.00000")
        Print #threadFile, "      Wrench Makeup Length: " & Format(tapered.WrenchMakeupLength, "0.00000")
    End If
End Sub

' Function used to create a printable string from a point or vector.
Private Function GetPointOrVector(pointOrVector As Object) As String
    GetPointOrVector = Format(pointOrVector.X, "0.00000") & ", " & _
                       Format(pointOrVector.Y, "0.00000") & ", " & _
                       Format(pointOrVector.Z, "0.00000")
End Function

Below is an example of the output of the program for one thread.

   Cylinder Geometry
      Cylinder Diameter: 4.46786
      Cylinder Position: 4.92743, -33.67887, -11.22845
      Cylinder Direction: 0.00000, 1.00000, 0.00000
   General Thread Info
      Designation: 1 13/16-12 UN
      Custom Designation: 1 13/16-12 UN
      Full thread depth: False
      Internal: True
      Metric: False
      RightHanded: True
      Thread Type: ANSI Unified Screw Threads
      Thread Type Identifier: ANSI Unified Screw Threads
      Direction: 0.00000, 1.00000, 0.00000
      Depth: 2.54000
         Position: 4.92743, 0.00000, -11.22845
   Standard Thread Info
      Class: 2B
      Max Major Dia.: 
      Min Major Dia.: 1.81250
      Max Minor Dia.: 1.74000
      Min Minor Dia.: 1.72200
      Nominal Size: 1.81250
      Pitch: 0.08333
      Max Pitch Dia.: 1.76620
      Min Pitch Dia.: 1.75840
      Tap Drill Dia.: 1.73440

The exception between running this with the part that contains the feature or a derived version of the part is that the location of the holes is handled slightly different.  The exception only comes into play when you have a tapped hole feature that was created using a sketch and creates more than one physical hole.  (There must have been a specific reason the API was implemented the way it is but I don’t remember what it was.)  The API to get thread information returns a collection of locations for the hole.  In most cases, this will be a collection of one and represents the thread information associated with the cylindrical face.  However, in the case where the feature is available, this collection will return the locations for all of the threads created by the hole feature.  This isn’t a problem by itself, but if you’re iterating over all cylindrical faces, each of the cylinders representing one of the holes of the feature will return this same set of points.  Because of this if you process each point you will get duplicate threads.  The code above handles this by finding the hole within the list that corresponds to the current cylindrical face being processed.  This isn’t a problem if the model has been derived, is an iFeature, or you’re using Apprentice.  In that case, every thread will always have a single position point.

Using Apprentice

Below is the dialog from a Visual Basic sample that demonstrates getting hole information from a specified part using Apprentice.  The full project can be downloaded here.  It was written referencing the Inventor 2018 interop library but it can easily be changed to work with older versions of Inventor because the capabilities it uses have been supported for a long time.

Hole Position and Depth

Most of the information in the report is describing details about the thread.  However, the position and depth of the thread are also described.  The ThreadInfo object supports the ThreadDirection,  FullThreadDepth, and ThreadBasePoints properties.  The ThreadBasePoints property returns the collection of points, as described above, which define the starting point of the thread in model space. The FullThreadDepth property is a Boolean property that indicates if the thread is the full length of the cylinder or not.  The ThreadDirection is a Vector object that defines both the direction and the length of the thread.  For example, if you have a hole and the thread is running in the negative Z direction for 1.5 inches the vector for FullThreadDepth will be (0.0, 0.0, -3.81).  Remember that all distances in the API are in centimeters so 3.81 cm is equal to 1.5 inches.

I clean up the information in the report by returning the direction as a unit vector (length of 1) and display the depth by getting the length of the vector.

Leave a Comment