Creating A Bounding Box Around A Solid: Assembly Mode

The code I copied from http://www.nxjournaling.com/content/creating-bounding-box-arround-solid is very good but it is accurate when applied to the displayed part only. In the assembly mode, it is not accurate as it does not create box around the correct directions.In other words, the box dimensions reported in displayed-part mode and assembly mode are not the same. How should the code be adjusted so the dimensions in assembly mode is the same as the displayed part mode?

Option Strict Off

Imports System

Imports NXOpen
Imports NXOpen.UI
Imports NXOpen.Utilities
Imports NXOpen.UF

Module make_bounding_block_of_selected_body_relative_to_wcs

Dim s As Session = Session.GetSession()
Dim ufs As UFSession = UFSession.GetUFSession()
Dim lw As ListingWindow = s.ListingWindow()

Sub Main()

Dim a_body As NXOpen.Tag = NXOpen.Tag.Null
Dim csys As NXOpen.Tag = NXOpen.Tag.Null
Dim target As NXOpen.Tag = NXOpen.Tag.Null
Dim blockFeature As NXOpen.Tag = NXOpen.Tag.Null

Dim min_corner(2) As Double
Dim directions(2, 2) As Double
Dim distances(2) As Double
Dim edge_len(2) As String

While select_a_body(a_body) = Selection.Response.Ok

ufs.Csys.AskWcs(csys)

ufs.Modl.AskBoundingBoxExact(a_body, csys, min_corner, directions, _
distances)

lw.Open()

lw.WriteLine("Min_corner: " & _
min_corner(0).ToString & ", " & _
min_corner(1).ToString & ", " & _
min_corner(2).ToString & ", ")

lw.WriteLine("X direction: " & _
directions(0, 0).ToString & ", " & _
directions(0, 1).ToString & ", " & _
directions(0, 2).ToString & ", ")
lw.WriteLine("X distance: " & _
distances(0).ToString)

lw.WriteLine("Y direction: " & _
directions(1, 0).ToString & ", " & _
directions(1, 1).ToString & ", " & _
directions(1, 2).ToString & ", ")
lw.WriteLine("Y distance: " & _
distances(1).ToString)

lw.WriteLine("Z direction: " & _
directions(2, 0).ToString & ", " & _
directions(2, 1).ToString & ", " & _
directions(2, 2).ToString & ", ")
lw.WriteLine("Z distance: " & _
distances(2).ToString)

edge_len(0) = distances(0).ToString()
edge_len(1) = distances(1).ToString()
edge_len(2) = distances(2).ToString()

ufs.Modl.CreateBlock(FeatureSigns.Nullsign, _
target, min_corner, edge_len, blockFeature)
End While

End Sub

Function select_a_body(ByRef a_body As NXOpen.Tag) As Selection.Response

Dim message As String = "Select a body"
Dim title As String = "Select a body"
Dim scope As Integer = UFConstants.UF_UI_SEL_SCOPE_ANY_IN_ASSEMBLY
Dim response As Integer

Dim view As NXOpen.Tag
Dim cursor(2) As Double
Dim ip As UFUi.SelInitFnT = AddressOf body_init_proc

ufs.Ui.LockUgAccess(UFConstants.UF_UI_FROM_CUSTOM)

Try
ufs.Ui.SelectWithSingleDialog(message, title, scope, ip, _
Nothing, response, a_body, cursor, view)
Finally
ufs.Ui.UnlockUgAccess(UFConstants.UF_UI_FROM_CUSTOM)
End Try

If response <> UFConstants.UF_UI_OBJECT_SELECTED And _
response <> UFConstants.UF_UI_OBJECT_SELECTED_BY_NAME Then
Return Selection.Response.Cancel
Else
ufs.Disp.SetHighlight(a_body, 0)
Return Selection.Response.Ok
End If

End Function

Function body_init_proc(ByVal select_ As IntPtr, _
ByVal userdata As IntPtr) As Integer

Dim num_triples As Integer = 1
Dim mask_triples(0) As UFUi.Mask
mask_triples(0).object_type = UFConstants.UF_solid_type
mask_triples(0).object_subtype = UFConstants.UF_solid_body_subtype
mask_triples(0).solid_type = UFConstants.UF_UI_SEL_FEATURE_BODY

ufs.Ui.SetSelMask(select_, _
UFUi.SelMaskAction.SelMaskClearAndEnableSpecific, _
num_triples, mask_triples)
Return UFConstants.UF_UI_SEL_SUCCESS

End Function

Public Function GetUnloadOption(ByVal dummy As String) As Integer

GetUnloadOption = UFConstants.UF_UNLOAD_IMMEDIATELY

End Function

How do I pass the component to the AskBoundingBoxExact instead of using the selection dialog?

AskBoundingBoxExact will only take one body as input, If you want to do it for all items in the assembly you can add all bodies to a for loop and you can do it. If you want to do it to only specific bodies you have to find a way to filter them using name or color or other properties. The difference between the displayed part and in assembly mode is due to the reference coordinates
" ufs.Csys.AskWcs(csys)"
you can add a function to allow the user to select which CSYS they want. This will give same result in both cases.

For going through all the bodies you can use the code below.

Function AskAllBodies(ByVal thePart As Part) As List(Of Body)

Dim theBodies As New List(Of Body)
Try
Dim aBodyTag As Tag = Tag.Null
Do
ufs.Obj.CycleObjsInPart(thePart.Tag, _
UFConstants.UF_solid_type, aBodyTag)
If aBodyTag = Tag.Null Then
Exit Do
End If

Dim theType As Integer, theSubtype As Integer
ufs.Obj.AskTypeAndSubtype(aBodyTag, theType, theSubtype)
If theSubtype = UFConstants.UF_solid_body_subtype Then
theBodies.Add(Utilities.NXObjectManager.Get(aBodyTag))
End If
Loop While True
Catch ex As NXException
lw.WriteLine(ex.ErrorCode & ex.Message)
End Try
Return theBodies
End Function

Regards,

Joe

Thanks, Alto. I tried to pass a workPart to get List(of Body) but I got this message Unhandled exception has occurred in a component in your application. If you click Continue, the application will ignore this error and attempt to continue.
Object variable or With block variable not set.

Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim workPart As NXOpen.Part = theSession.Parts.Work
MsgBox(AskAllBodies(workPart)(0).Name)
End Sub

Based on the little code that you've posted, I'd guess that the error is one of two things:
1) The NXOpen dll is not referenced properly in your project.
or
2) The subroutine does not have access to the "theSession" variable. It will need to be declared with the proper scope, or a reference needs to be passed to the subroutine for it to have access.

Could you ask an example code on how to run the function? (1) I am very sure I added the dll correctly as I have been building other codes as well. (2) I added Dim theSession As Session = Session.GetSession() to my sub, the program could be built but still same error.

If you post full code only it is easy to identify the error where it went wrong. This following one is working good for me.

Option Strict Off

Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UI
Imports NXOpen.Utilities
Imports NXOpen.UF

Module BoundBox

Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
Dim displayPart As Part = theSession.Parts.Display
Dim theUI As UI = UI.GetUI
Dim ufs As UFSession = UFSession.GetUFSession
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

Dim a_body As NXOpen.Tag = NXOpen.Tag.Null
Dim csys As NXOpen.Tag = NXOpen.Tag.Null
Dim target As NXOpen.Tag = NXOpen.Tag.Null
Dim blockFeature As NXOpen.Tag = NXOpen.Tag.Null

lw.Open()

Dim min_corner(2) As Double
Dim directions(2, 2) As Double
Dim distances(2) As Double
Dim edge_len(2) As String

'Retrive Bodies here from function
Dim MybodyList As List(Of Body) = New List(Of Body)
MybodyList = AskAllBodies(workPart)
If MybodyList.Count = 0 Then
MsgBox("There are No bodies to measure in Work Part")
End If

For Each Mybody As Body In MybodyList
a_body = Mybody.Tag

ufs.Csys.AskWcs(csys)

ufs.Modl.AskBoundingBoxExact(a_body, csys, min_corner, directions, _
distances)

lw.WriteLine("Min_corner: " & _
min_corner(0).ToString & ", " & _
min_corner(1).ToString & ", " & _
min_corner(2).ToString & ", ")

lw.WriteLine("X direction: " & _
directions(0, 0).ToString & ", " & _
directions(0, 1).ToString & ", " & _
directions(0, 2).ToString & ", ")
lw.WriteLine("X distance: " & _
distances(0).ToString)

lw.WriteLine("Y direction: " & _
directions(1, 0).ToString & ", " & _
directions(1, 1).ToString & ", " & _
directions(1, 2).ToString & ", ")
lw.WriteLine("Y distance: " & _
distances(1).ToString)

lw.WriteLine("Z direction: " & _
directions(2, 0).ToString & ", " & _
directions(2, 1).ToString & ", " & _
directions(2, 2).ToString & ", ")
lw.WriteLine("Z distance: " & _
distances(2).ToString)

edge_len(0) = distances(0).ToString()
edge_len(1) = distances(1).ToString()
edge_len(2) = distances(2).ToString()

ufs.Modl.CreateBlock(FeatureSigns.Nullsign, _
target, min_corner, edge_len, blockFeature)
Next

End Sub

Function AskAllBodies(ByVal thePart As Part) As List(Of Body)

Dim theBodies As New List(Of Body)
Try
Dim aBodyTag As Tag = NXOpen.Tag.Null
Do
ufs.Obj.CycleObjsInPart(thePart.Tag, _
UFConstants.UF_solid_type, aBodyTag)
If aBodyTag = NXOpen.Tag.Null Then
Exit Do
End If

Dim theType As Integer, theSubtype As Integer
ufs.Obj.AskTypeAndSubtype(aBodyTag, theType, theSubtype)
If theSubtype = UFConstants.UF_solid_body_subtype Then
theBodies.Add(Utilities.NXObjectManager.Get(aBodyTag))
End If
Loop While True
Catch ex As NXException
lw.WriteLine(ex.ErrorCode & ex.Message)
End Try
Return theBodies
End Function

Public Function GetUnloadOption(ByVal dummy As String) As Integer

GetUnloadOption = UFConstants.UF_UNLOAD_IMMEDIATELY

End Function
End Module

Regards,

Joe

Thanks, Joe. Let me review your code and study to see if I know why I could not get my code worked.

GTAC also has some example code. Search the GTAC solution center for "bounding box" to find several examples. Here are 2:

http://solutions.industrysoftware.automation.siemens.com/view.php?si=nx_...

http://solutions.industrysoftware.automation.siemens.com/view.php?si=nx_...

Link to solution center:
https://solutions.industrysoftware.automation.siemens.com/

I will try to figure these 2 things out sometime tonight as all of the licenses @ my company are occupied now. But just in case you know or you can provide quick hints for 2 questions on the 2nd link

(1) How could I adjust the code to run in Make Displayed Part mode? It does not return anything when I run the code for the part (Assembly --> Right Click --> Make Displayed Part).

(2) could I use the coordinates of the parts' instead of the main assembly coordinate? This will help me get the dimensions of the boxes correctly.

Thank you very much.

1) This thread has lots of code and links to code in it. You'll need to specify which journal that you are running on a piece part before anyone can give a specific answer.

2) You will need to calculate the bounding box for the prototype body rather than the assembly occurrence body. The code in one of the GTAC links above shows how to do that. Note: I had pasted an incorrect link in the previous reply - I have edited the reply to correct the link.

1. Sorry, I meant https://solutions.industrysoftware.automation.siemens.com/view.php?si=nx...
However, I looked into the code from your new link and I think it should already solve the problem.

2. Thanks.