Differences in selecting bodies via visibleObjects vs Session.Parts

Hello,

I was wondering if anyone could help with a more in-depth description / documentation on bodies. Currently I'm getting interesting results when selecting bodies from visible Objects compared to the current session's partCollection object.

When selecting bodies using the current Sessions partCollection object:

For Each part As Part In NxSession.Parts
'ejComp is a custom object
If String.Equals(part.Leaf, ejComp.DisplayName) Then
For Each dispObj As DisplayableObject In part.Bodies
Dim bodyObj As Object = CType(dispObj, Object)
Lw.WriteLine("For body: " & bodyObj.JournalIdentifier)
body.highlight()
'Do other processing
Next
End If
Next

I get different bodies than when I select these bodies by making them the only displayed objects and running:


Dim visibleObjects() As DisplayableObject
visibleObjects = _workPart.Views.WorkView.AskVisibleObjects()
For Each displayableObject As DisplayableObject In visibleObjects
If TypeOf displayableObject Is Body Then
Dim body As Body = displayableObject
Lw.WriteLine("Body: " & body.JournalIdentifier)
body.highlight()
'Do other processing
End If
Next

The JournalIdentifers do not match, however both will cause the seemingly same bodies to highlight.
What causes these two forms of selections to return different bodies?

It's not clear that the lists of bodies are actually different. Journal identifiers don't mean much, and, as you say, the same set of bodies seems to be getting highlighted. Try printout using body.ToString instead. This will give the body's tag, which is a much more reliable identifier than its journal identifier.

Here is some output I put together using the bodies.toString() method instead of the journal identifier. This first bit is selecting by component name in the Session's part collection:

For component: 4707_ej-pin3
For body: Body 65772
For component: 4707_ej-pin2
For body: Body 65781
For component: 4707_ej-pin1
For body: Body 65788
For component: 4707_ej-pin0
For body: Body 65796
For component: 4707_ej-pin
For body: Body 65804
For body: Body 65805
For body: Body 65802
For body: Body 65800
For component: 4707_Misumi_Sq_Ej-Pin
For body: Body 65827
For body: Body 65826
For body: Body 65817
For body: Body 65816

*********************
NOW SELECTING WITH VISIBLE OBJECTS
*********************

Owning Component: 4707_Misumi_Sq_Ej-Pin
For body: Body 47464
Owning Component: 4707_ej-pin
For body: Body 45362
Owning Component: 4707_ej-pin0
For body: Body 47612
Owning Component: 4707_ej-pin1
For body: Body 45713
Owning Component: 4707_ej-pin2
For body: Body 45579
Owning Component: 4707_ej-pin3
For body: Body 45558

So the body tags / toStrings are not the same yet they are still highlighting the same parts. I also added a body.OwningComponent.DisplayName to show that the code is looking at the same components.

NX 8.5
NX 9.0

For each body you encounter, in your program output, please add:
writeline("body is occurrence: " & theBody.IsOccurrence.ToString)

I've not run the experiment myself, but I'm guessing that in one case you are getting the underlying body and in the other case you are getting the assembly occurrence of the body.


For Each part As Part In NxSession.Parts
'ejComp is a custom object
If String.Equals(part.Leaf, ejComp.DisplayName) Then
Lw.WriteLine("For Component: " & part.Leaf)
For Each dispObj As DisplayableObject In part.Bodies
Dim bodyObj As Object = CType(dispObj, Object)
Lw.WriteLine(" For body: " & bodyObj.Tag)
Lw.WriteLine(" Body is an occurence: " & bodyObj.IsOccurrence.ToString & vbCrLf)

'Do other processing
Next
End If
Next

Produces the output:

  • For Component: 4707_ej-pin3
  • For body: 190756
  • Body is an occurence: False
  • For body: 190759
  • Body is an occurence: False
  • For body: 190755
  • Body is an occurence: False
  • For body: 190752
  • Body is an occurence: False
  • For Component: 4707_ej-pin2
  • For body: 190720
  • Body is an occurence: False
  • For body: 190765
  • Body is an occurence: False
  • For body: 190760
  • Body is an occurence: False
  • For body: 190763
  • Body is an occurence: False
  • For Component: 4707_ej-pin1
  • For body: 190770
  • Body is an occurence: False
  • For body: 190724
  • Body is an occurence: False
  • For body: 190769
  • Body is an occurence: False
  • For body: 190721
  • Body is an occurence: False
  • For Component: 4707_ej-pin0
  • For body: 190754
  • Body is an occurence: False
  • For body: 190761
  • Body is an occurence: False
  • For body: 190747
  • Body is an occurence: False
  • For body: 190768
  • Body is an occurence: False
  • For Component: 4707_ej-pin
  • For body: 190085
  • Body is an occurence: False
  • For body: 190086
  • Body is an occurence: False
  • For body: 190083
  • Body is an occurence: False
  • For body: 190081
  • Body is an occurence: False
  • For Component: 4707_Misumi_Sq_Ej-Pin
  • For body: 190108
  • Body is an occurence: False
  • For body: 190107
  • Body is an occurence: False
  • For body: 190098
  • Body is an occurence: False
  • For body: 190097
  • Body is an occurence: False

Now with visible objects:


Dim visibleObjects() As DisplayableObject
visibleObjects = _workPart.Views.WorkView.AskVisibleObjects()
For Each displayableObject As DisplayableObject In visibleObjects
If TypeOf displayableObject Is Body Then
Dim body As Body = displayableObject
Lw.WriteLine("For component: " & body.Prototype.OwningPart.Leaf)
Lw.WriteLine(" For Body: " & body.Tag)
Lw.WriteLine(" body is an occurence: " & body.IsOccurrence.ToString)
Lw.WriteLine("")
'Do other processing
End If
Next

Produces the output:

  • For component: 4707_Misumi_Sq_Ej-Pin
  • For Body: 293843
  • body is an occurence: True
  • For component: 4707_ej-pin
  • For Body: 392299
  • body is an occurence: True
  • For component: 4707_ej-pin0
  • For Body: 293900
  • body is an occurence: True
  • For component: 4707_ej-pin1
  • For Body: 392650
  • body is an occurence: True
  • For component: 4707_ej-pin2
  • For Body: 392516
  • body is an occurence: True
  • For component: 4707_ej-pin3
  • For Body: 392495
  • body is an occurence: True

NX 8.5
NX 9.0

In the first code snippet, the term "component" is a bit misleading; you are actually processing the solid bodies in the part files in session.

In the second code snippet, the term "component" is accurate; you are processing the bodies in the displayed assembly file. Each body shown in an assembly is an occurrence of the body in the owning part file.

The solid bodies in the part files vs the bodies in the displayed assembly file.

I am new to NX, I understand the displayed assembly file is the component in the assembly navigator that is displayed in your NX session. However I don't have any clue how it differs from the part files in session. And I don't know where to turn for answers, can you help me out with some documentation or explanation?

Thank you

NX 8.5
NX 9.0

The NX help files give a quick overview at:
CAD -> Assemblies -> intro to NX assemblies
Take note of the "component objects", "component part files", and "component occurrences" pages in that section.

In short, the component acts like an invisible box that holds references to geometry in the part file. The component itself doesn't own or control the geometry; when the part geometry is modified, every occurrence is updated because they point back to the original geometry in the part file. So if you have an assembly model of a toy car, there may be 4 wheels in the assembly, but the geometry is controlled by a single wheel part file. The assembly needs to keep track of each of the 4 wheels so each occurrence body gets its own identifier which is different from the identifier of the solid body in the part file.

I hope this helps to illuminate rather than obscure the issue...

hi,
I have done a little coding, but won't have blends only closed cylinders / holes, in my collection:


For Each displayableObject As DisplayableObject In visibleObjects
If TypeOf displayableObject Is Body Then
Dim body As Body = displayableObject
If ufs.Assem.IsOccurrence(body.Tag) = False Then
For Each face1 As Face In body.GetFaces()
If face1.SolidFaceType = Face.FaceType.Cylindrical Then

ReDim Preserve localFaces(counter)
counter += 1
Face1.Highlight()
End if
Next
End If
End If
Next

what is to add to get rid of blends in this selection?
thanks in advance

If you are dealing with a parameterized model, you can query the face to see if it belongs to a hole feature or a blend feature.

I'll assume you are working with a non-parameterized file; the situation here is more interesting. I don't have any code that proves this will work, but here is what I would try:
Use the function UF_MODL_ask_edge_faces to get references to the other faces that share the circular edges of the cylindrical face. Pick a point near the edge on the cylindrical face and query the face normal at that point (using UF_MODL_ask_face_props or similar). Pick a point on the adjoining face near the first point and again query the face normal at that point. If the angle between the normal vectors is small, the cylindrical face is likely a blend. If the angle between the vectors is large, the cylindrical face is likely a hole or boss. If the normal vector points away from the centerline of the cylindrical face it is a boss, otherwise it is a hole.

Yes thank you! If I understand you correctly, components / parts are really just an object oriented way to set up designs. The part acts as the class that holds methods (part info), and the assemblies are just references to the objects (parts). Which completely explains the interesting results I was originally getting. And the isOccurence check lets you know if the current object is a reference to the part file.

Thank you for your help, I think I'm starting to understand this better.

NX 8.5
NX 9.0

I am trying to get data from specific component's geometry. This is proving to be a difficult task.
I need to get the center of an arc with respect to the absolute coordinate system. Currently I have been using the UFSession.Eval class for this. The problem is, the component class does not have a "getBodies" call, and cannot be converted into a body through casting. Is there another way to get component specific data? (faces, bodies, edges, arcs, curves). The part files won't work for this because there is no relation between the part's and assemblies positional data.

NX 8.5
NX 9.0

Update: I ended up using the visibleObjects() call, therefore I had DisplayableObjects and could process everything from there. Thanks for the help.

NX 8.5
NX 9.0

I have a question regarding component bodies / part file bodies and thought this thread was a good place to ask, I'll do my best to explain my situation, please let me know if I'm using bad terminology, thanks.

I have a reference to a component in my assembly, I am unblanking the component so I can askVisibleObjects to obtain the component bodies. However, unblanking the component does not unblank the component bodies, so, in some cases I could have an unblanked component with a blanked component solid body, and the askVisibleObjects code will not return the body.

Due to this scenario I'm looking for an alternative method to find a reference to all component bodies so I can unblank them using a unblank() call. Since askVisibleObjects is off the table and the component class doesn't have a reference to component bodies I'm kind of stuck.

An alternative route I have started to explore is using the component's prototype object to obtain the part file from which the component was created from and look at those solid bodies. However, and this is my question, will unblanking the solid bodies from a part class inherently unblank the component solid bodies?

NX 8.5
NX 9.0

"will unblanking the solid bodies from a part class inherently unblank the component solid bodies?"

Short answer: No, you will need to unblank the occurrence body(ies) in the assembly file.

Longer answer: One way to get the occurrence body is to query what reference set the component uses in the assembly, get a reference to the component prototype (the part file) and query the bodies used in the given reference set, and finally, use the {component}.FindOccurrence({prototype body}) method on the prototype bodies to get the corresponding occurrence bodies.

p.s. The above strategy won't work on deformed components or promoted bodies.

Thank you for your response, I'll have to test this method out.

In the meantime I've concluded that for the time being the component prototype bodies will suffice for my program requirements, and have found something very interesting. It will be best to describe with some code:

Dim part as Part = component.prototype ' ensure prototype is a part class
Dim bodyCollection as Body() = part.bodes.toarray() ' get an array of all the bodies from the component's prototype
lw.writeLine(bodyCollection.length)

So this code works to get the body I want, however, I've noticed if the component's reference set is set to empty reference set regardless that I'm collecting the bodies from the component's prototype, the length of the body collection array comes out to 0. Changing the reference set to anything provides a length greater than 0.

Can you help me understand why?

NX 8.5
NX 9.0

I'm not seeing the same results that you describe. I'm running NX 9.0.3.4 and the code below returns the same number of bodies whether the used reference set is "empty" or something else.

Option Strict Off
Imports System
Imports NXOpen

Module Module1

Sub Main()

Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()

Const undoMarkName As String = "report component body count"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

'report first level components
For Each child As Assemblies.Component In workPart.ComponentAssembly.RootComponent.GetChildren()
lw.WriteLine(child.DisplayName)
lw.WriteLine("reference set: " & child.ReferenceSet)
Dim childPart As Part = child.Prototype
lw.WriteLine("number of bodies: " & childPart.Bodies.ToArray.Length.ToString)
lw.WriteLine("")
Next

lw.Close()

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

I've noticed the issue I had described earlier ONLY applies if the component was loaded in as "Empty". For example, I open a new part, that part defaults to last reference set saved which was "Empty", the code returns 0 bodies, then I can change the reference set to "Entire Part", then directly change the reference set back to "Empty" and run the code again and it returns 1+ bodies.

Based on this, I'm speculating some partial loading setting is causing the original part file bodies to not be accessible until you change the ref. set?

NX 8.5
NX 9.0

The parts that I tested on were fully loaded; I think that you are correct that the difference we are seeing has to do with partial loading. The partial loading option will only load the geometry necessary from the part. If the component loads with the "empty" reference set in use, there is no reason to load any geometry until the reference set is changed. If desired, you could use the part's .LoadFully (or .LoadThisPartFully) method to fully load the part before querying the body collection.

Thank you, loading the part fully seems to have fixed the issue and is a more elegant solution than programmatically changing the reference sets.

Appreciate the help, cheers.

NX 8.5
NX 9.0