2022年4月21日星期四

Journaling In NX

 http://www.nxjournaling.com/content/getting-started-journaling-nx


Beginning Journaling Using NX Journal

(menu locations in this tutorial are based on NX 7.5, programming language is Visual Basic)

Where to start? The best place to start writing a journal is with the journal recorder. This wonder of modern technology will translate your interactions with NX to journal code, in your language of choice! (this article will use Visual Basic, or VB for short). If you are following along, let’s make sure the journal output language is set to VB before we start.

Setting the Journal Programming Language:
In the menu, choosePreferences -> User Interface...; on the Journaltab choose Visual Basic as the language and make sure the Insert Menu/Dialog Box Commentsoption is checked.

 

Recording the Journal:

For starters, let’s assume you want to change the color of a specified solid body. Start by recording a journal Tools -> Journal -> Record... and specify a file name for your new journal, preferably something that describes its function such as “change_color.vb”. Once the journal recording process starts, you may notice a change in your toolbars and menus. The green marks on the toolbar icons/menu items indicate that journal recording is fully supported by this command; yellow marks indicate the command is partially supported.  

Now that recording is under way, choose Edit -> Object Display... from the menu, pick the solid body of interest, and pick a new display color. OK the changes and then stop the journal recordingTools -> Journal -> Stop recording.

Time to test out the journal. Change the color of the solid body, or perform an Undo to get the original color back; now navigate to Tools -> Journal -> Play... and browse to your new journal file. Select your journal from the list and press the Playbutton. After a short pause, your solid body will change to the color you picked when you recorded the journal.

 

Understanding the New Journal Code:
Open a Windows file explorer window and browse to your new journal file, open it with a text editor (such as notepad) and take a peek at what goes on behind the scenes. Don’t worry if this looks like mumbo jumbo right now, we’ll step through it to see what is going on.  I created a new file and inserted a block feature, then followed the steps outlined above; here is the resulting journal code:

Option Strict Off
Imports System
Imports NXOpen
 
Module NXJournal
Sub Main
 
Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
 
Dim displayPart As Part = theSession.Parts.Display
 
' ----------------------------------------------
'   Menu: Edit->Object Display...
' ----------------------------------------------
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Invisible, "Start")
 
theSession.SetUndoMarkName(markId1, "Class Selection Dialog")
 
Dim markId2 As Session.UndoMarkId
markId2 = theSession.SetUndoMark(Session.MarkVisibility.Invisible, "Class Selection")
 
theSession.DeleteUndoMark(markId2, Nothing)
 
theSession.SetUndoMarkName(markId1, "Class Selection")
 
theSession.DeleteUndoMark(markId1, Nothing)
 
' ----------------------------------------------
'   Dialog Begin Edit Object Display
' ----------------------------------------------
' ----------------------------------------------
'   Dialog Begin Color
' ----------------------------------------------
Dim markId3 As Session.UndoMarkId
markId3 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Edit Object Display")
 
Dim displayModification1 As DisplayModification
displayModification1 = theSession.DisplayManager.NewDisplayModification()
 
displayModification1.ApplyToAllFaces = False
 
displayModification1.ApplyToOwningParts = False
 
displayModification1.NewColor = 103
 
Dim objects1(0) As DisplayableObject
Dim body1 As Body = CType(workPart.Bodies.FindObject("BLOCK(0)"), Body)
 
objects1(0) = body1
displayModification1.Apply(objects1)
 
displayModification1.Dispose()
' ----------------------------------------------
'   Menu: Tools->Journal->Stop Recording
' ----------------------------------------------
 
End Sub
End Module

Option Strict Off means the code will allow some types of implicit conversions, but really it isn’t something we’ll need to worry about right now.

Next up are the Imports statements, which import a specified namespace. It is important to note that these statements do not add more functionality to your code (ie they are not analogous to references to external libraries); rather they allow certain declarations to be abbreviated. In addition to NX code, journals are linked by default to certain .NET libraries. These libraries are:

  • mscorlib.dll
  • System.dll
  • System.Windows.Forms.dll
  • System.Drawing.dll

Module is declared next with the default name of NXJournal. A module is a container where all the code lives, if you skip down to the end of the journal you will see an End Module statement. Any code you add must be between the Module... End Module statements. You can change the module name to something more descriptive if you like, but since there is only one module in our journal, the default name is just fine. The module also determines the scope of variables and code it contains, but this is a topic for another day.

Sub Main is the block of code that gets called when your journal is run. As your journals get longer and more complex, it is helpful to break the code into multiple subroutines and functions and call them as necessary. Sub Main is where the core logic will reside. Every journal must have a Sub Main.

The next 3 lines initialize the session, display part, and work part for us to reference in our journal. Part and Session are classes in NXOpen, these object variables will give us access to other objects in NX.

All of the code we have looked at so far is included in every recorded journal. If you want to start a journal from scratch, including this code is a good idea.

A comment on comments:
A single apostrophe or the keyword rem indicates the start of a comment, the comment continues to the end of the line. Anything contained in the comment will be ignored by the compiler. Comments serve useful functions:

  • They are most often used to document the code. Use comments to explain tricky bits of code or document the input/output requirements of functions and subroutines. Comments are also often used at the beginning of the code file to explain the who when and what: who wrote the program, when they wrote the program, and what problem the code solves or what process the code automates.
  • Comments are also useful in the debugging process. Use comments to temporarily disable a line or entire block of code to try out an alternative solution without having to delete code that may yet prove useful.

Next up in the code, is the creation of a couple of useless undo marks. As you record and modify journals you will find that the journal recorder often throws in extra bits of code. The undo marks, markId1 and markId2, are good examples of this. The undo marks are created, named, renamed, and finally deleted (set to ‘nothing’) ultimately having no effect on the finished journal. If your journal contains similar code, feel free to delete it.

Creating an undo mark is a good way to start a journal as it will allow any changes made by the journal to be undone if necessary. MarkId3 is the real undo mark here. You can see the string “Edit Object Display” as one of the arguments when the id mark is set. This string is what will appear in the undo list after the journal is run. If the undo function is used, the model will roll back to the state it was in when the undo mark was set in code. It is a good idea to set an undo mark before trying to modify features; if there are any errors during the model update, it can roll back to a known state. Multiple undo marks can be set, which comes in handy in larger multi-step journals; changes can be rolled back in chunks rather than an all or nothing approach.

When declaring variables, this is the normal pattern:

Dim [variable name] as [variable type]

so the line:

Dim displayModification1 As DisplayModification

tells the compiler to set aside a block of memory large enough for a DisplayModification object and the name used to refer to it will be displayModification1. A DisplayModification is a temporary helper NX object that assists in changing the display attributes of one or more objects.

Now that we have a display modification object we can set our preferences:

displayModification1.ApplyToAllFaces = False
displayModification1.ApplyToOwningParts = False
displayModification1.NewColor = 103

As you can see, I picked color 103, cornflower blue, as my new object color. At this point our solid body has not yet changed, in fact the journal doesn’t yet know what object(s) these changes are going to apply to. Which brings us to:

Dim objects1(0) As DisplayableObject
Dim body1 As Body = CType(workPart.Bodies.FindObject("BLOCK(0)"), Body)
objects1(0) = body1

The display modification object requires an array of objects of the type DisplayableObject to work on. The first line creates a 1 element array (arrays are zero based) of DisplayableObjects, line 2 records the solid that I selected (your line of code may be different), and line 3 assigns the body to the array that will be used with the display modification object.

Now that the options are set and the object is specified we can apply our desired changes with

displayModification1.Apply(objects1)

Now that the work is done, we can delete the temporary display modification object and free up the memory it used by calling its Dispose method:

displayModification1.Dispose()

At this point the journal exits and we can see the results in the modeling window. However, should you try to run this journal in a different file you would be met with an error message.

Hand Editing the Journal:
The reason that the journal in its current state will end in a error message when applied to a different file is that your journal is looking for the exact solid that you selected when the journal was recorded. This behavior is referred to as ‘selection stickiness’ and its removal is one of the main reasons for hand-editing journal code. You can greatly increase the usefulness of your code by replacing the hard coded reference to a specific object with a more general selection routine.

Below you will find the same script without the useless undo marks and with the selection stickiness removed.  This journal can now be used on various files to change the selected solid body’s color.

Option Strict Off
 
Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpenUI
 
Module NXJournal
Sub Main
 
Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
Dim displayPart As Part = theSession.Parts.Display
Dim ufs As UFSession = UFSession.GetUFSession()
 
Dim markId3 As Session.UndoMarkId
markId3 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Edit Object Display")
 
Dim displayModification1 As DisplayModification
displayModification1 = theSession.DisplayManager.NewDisplayModification()
displayModification1.ApplyToAllFaces = False
displayModification1.ApplyToOwningParts = False
displayModification1.NewColor = 103
 
Dim objects1(0) As DisplayableObject
'Dim body1 As Body = CType(workPart.Bodies.FindObject("BLOCK(0)"), Body)
Dim body1 As Body = SelectSolid()
objects1(0) = body1
displayModification1.Apply(objects1)
displayModification1.Dispose()
 
End Sub
 
Function SelectSolid() As Body
 
    Dim ui As UI = ui.GetUI
    Dim message As String = "Select solid body"
    Dim title As String = "Selection"
 
    Dim scope As Selection.SelectionScope = Selection.SelectionScope.WorkPart
    Dim keepHighlighted As Boolean = False
    Dim includeFeatures As Boolean = True
 
    Dim selectionAction As Selection.SelectionAction = _
       Selection.SelectionAction.ClearAndEnableSpecific
 
    Dim selectionMask_array(0) As Selection.MaskTriple
    With selectionMask_array(0)
       .Type = UFConstants.UF_solid_type
       .Subtype = 0
       .SolidBodySubtype = UFConstants.UF_UI_SEL_FEATURE_SOLID_BODY
    End With
 
    Dim selectedObject As NXObject = Nothing
    Dim cursor As Point3d
 
    ui.SelectionManager.SelectObject(message, title, scope, _
                                    selectionAction, includeFeatures, _
                                    keepHighlighted, selectionMask_array, _
                                    selectedObject, cursor)
 
    Dim solid As Body = CType(selectedObject, Body)
 
    If solid Is Nothing Then
       Return Nothing
    End If
 
    Return solid
 
End Function
 
End Module

Download the code.

Recap:
In review you can see how we can use NX Journal to create a basic journal designed to change the color of a solid body.  Then we evaluated the code generated by the journal recorder and modified the code to be of broader use with just a few hand coded manipulations. The resulting journal can now change the body color of any part.

Objectives:
We learned how to record a journal. How to interpret the code behind the NX Journal and how to adjust the code to allow a journal to be useful across multiple files.


===========================================


How I Stopped Worrying And Learned To Love The Help File

Or, How To Become An Alchemist In 6 Easy Steps

Clear instructions can ease the most difficult of tasks; conversely, even a simple task is frustratingly difficult when you don’t know where to start. If you have had an idea for a journal, but the commands were not supported by the journal recorder; chances are good you have experienced such frustration. As an absolute last resort (c’mon admit it!), you open the help file, net_ref.chm and feel as if you have just opened the Emerald Tablet; you glimpse a wealth of knowledge hiding behind a wall of impenetrable language.

I’ve developed a sort of strategy when using the help file. It is not a perfect system and I’m constantly looking for ways to improve it, but as of right now, here it is:

  1. Try to record a journal.
  2. Use the search (or index) function to look up the object (class) you are interested in.
  3. Use the search (or index) function to look up the builder for the object you are interested in.
  4. Step through the process in interactive NX to look for new, relevant search terms.
  5. Use the search function and look at the top returned items.
  6. Browse through the UF section of the help file.

As an example, I was working on a journal that would identify any and all dimensions that had their values overridden with manual text. To find these in interactive NX, you can go to Information -> Other -> Object-specific... then choose Dimensions with Manual text. This will highlight all the dimensions that have manual text.

Using the steps outlined above gave the following results:

  1. The journal recorder did not produce anything useful. When the journal recorder produces code, you can either edit the output to get what you want or at least pick out some key words to search for in the help file to get more information (the class names of the objects it creates are good terms to search on).
  2. Knowing from previous experience there is a Dimension class, I looked it up with the index function. From here I found the .GetDimensionText and .SetDimensionText methods but I could find nothing that would indicate if the text was automatic or manual.
  3. The DimensionBuilder has its own properties and methods, but again, nothing that would indicate if the text was manual.
  4. In interactive NX, I used Information -> Object to inspect a few dimensions (some with automatic text and some with manual text). The first value in the OBJECT SPECIFIC INFORMATION section is Text Placement. Initially I thought this referred to the dimension style options of automatic placement, manual placement - arrows in, and manual placement - arrows out; but I noticed for all the manual dimensions this was reported as “Manual Text” and for the automatic dimensions this was reported as “Automatic Text”. Back in the Dimension class entry in the help file, if you follow the .GetDimensionPreferences link through to the DimensionPreferences class you will see a .TextPlacement property. This .TextPlacement property refers to the dimension style options mentioned earlier and not to automatic or manual text as the information window does. I think it is simply a case of poor wording in the information window (at least in the english version of NX).
  5. I used the search function and entered the term manual text. The search returns 55 topics, many of which look like they are CAM related. So, ignoring such topics as DrillBurnishingToolBuilder class, leaves about 20 topics to look through. Many of these remaining topics start with uf_, which indicates it is in the older UF documentation. It is preferred to use the .NET function if one exists that does what you want. You can use the UF functions, but in general they are a bit trickier to use. Many of the uf_ topics are quite large; to search within a topic hold down the control key and press the F button. This will bring up a Find dialog box that will allow you to search within the topic. Searching within uf_drf turns up functions that allow you to retrieve the dimension text and change the dimension text and most interestingly, a description of a 100 element array called mpi that holds information about a dimension. Item 7 in this list (actually item 8 since the array is zero based, but we’ll ignore that for now) is TEXT ENTRY MODE, and in the description it states: “For UF_DRF_ask/set_object_preferences, this only reports or sets the text to be manual or automatic. It does not report whether the object has appended text.” Score! Now we just need some more information on this UF_DRF_ask_object_preferences function.
  6. Fortunately it didn’t come to this, but as a last resort I’d open up the Contents tab of the help file and start poking around the User Function Reference section (specifically the drafting and/or drawing section, for the given example). This is a tedious process but you may stumble upon something interesting that you can make use of in the future. When I run across such a gem, I take a screenshot (of at least the function name and description) and file it away for future reference - Microsoft OneNote is fabulous for this purpose.
     

Searching on uf_drf_ask_object_preferences returns four topics, one of which is AskObjectPreferences Method. This is the .NET wrapper for the UF function, the function signature is below:

Public Sub AskObjectPreferences ( _
    drf_object_tag As Tag, _
    <OutAttribute> mpi As Integer(), _
    <OutAttribute> mpr As Double(), _
    <OutAttribute> ByRef radius_val As String, _
    <OutAttribute> ByRef diameter_val As String _
)

What this tells us is when we call the function, we need to pass in five variables; those marked <OutAttribute> will be used to return information from the function (we will pass in empty variables for the function to use as output). So, we’ll have to pass in the tag of the dimension we are interested in (drf_object_tag), an array of integers (mpi), an array of doubles (mpr), and two string variables (radius_val and diameter_val). The help file politely provides a link to the UF_DRF_ask_object_preferences documentation; following this link we learn that the integer array needs to be 100 elements and the double array needs to be 70 elements. Following the “drafting parameters” link brings us back to a description of what the returned array values mean. The array value at offset 7 (mpi(7)) represents the text entry mode and can take on 1 of 4 values:

1 = Only automatic text
2 = Automatic text, with appended text
3 = Only manual text
4 = Manual text, with appended text

Pulling all this information together leads to the following key lines of code:

ufs.Drf.AskObjectPreferences(partDimension.Tag, mpi, mpr, radius_value, diameter_value)
 
If mpi(7) = 3 OrElse mpi(7) = 4 Then
 
'dimension has manual text
 
'do something with it
End if

The entire journal is shown below:

'NXJournaling.com
'May 31, 2012
'When run, the journal will highlight all dimensions in the part that have manually entered text.
'The listing window will report the dimension text, view, and sheet for each dimension
'with manually entered text.
 
Option Strict Off
Imports System
Imports NXOpen
Imports NXOpen.UF
 
Module Report_Dimensions_manual_text
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim ufs As UFSession = UFSession.GetUFSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim displayPart As Part = theSession.Parts.Display
       Dim lw As ListingWindow = theSession.ListingWindow
       Dim mpi(99) As Integer
       Dim mpr(69) As Double
       Dim radius_value As String
       Dim diameter_value As String
       Dim manualTextCount As Integer = 0
       Dim dictionary As New Collections.Generic.Dictionary(Of String, String)
       Dim viewList() As View
       Dim text1() As String
       Dim text2() As String
 
       lw.Open()
 
       For Each sheet As Drawings.DrawingSheet In workPart.DrawingSheets
           viewList = sheet.GetDraftingViews
           For Each myView As View In viewList
               dictionary.Add(myView.Name, sheet.Name)
           Next
       Next
 
       Dim partDimensions() As Annotations.Dimension
       partDimensions = workPart.Dimensions.ToArray
       If partDimensions.Length > 0 Then
           For Each partDimension As Annotations.Dimension In partDimensions
               ufs.Drf.AskObjectPreferences(partDimension.Tag, mpi, mpr, radius_value, diameter_value)
 
               If mpi(7) = 3 OrElse mpi(7) = 4 Then
                   'dimension has manual text
                   partDimension.Highlight()
                   lw.WriteLine("Sheet: " & dictionary(partDimension.GetAssociativity(1).ObjectView.Name))
                   lw.WriteLine("View: " & partDimension.GetAssociativity(1).ObjectView.Name)
                   partDimension.GetDimensionText(text1, text2)
                   Dim j As Integer
                   lw.WriteLine("Dimension Text: ")
                   For j = text1.GetLowerBound(0) To text1.GetUpperBound(0)
                       lw.WriteLine("  " & text1(j))
                   Next
                   manualTextCount += 1
                   lw.WriteLine("")
               End If
           Next
           If manualTextCount > 0 Then
               MsgBox(manualTextCount & " dimensions have manual text and have been highlighted")
           Else
               MsgBox("There are no dimensions with manual text in the part")
           End If
       Else
           MsgBox("There are no dimensions in the work part")
       End If
 
       lw.Close()
 
    End Sub
 
    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

The other bit of code I had some problems with was reporting which sheet a given dimension is on. I was hoping there was a property or method that would directly return what sheet the dimension is on. Looking at the Dimension class documentation didn’t turn up any such method, but while I was there I did find out how to return the view name the dimension is associated to. From there I was hoping the View class had a property or method to return the sheet it belonged to, but again, no such luck. However, while searching through the DrawingSheet class, I did find the .GetDrawingViews() method that returns a collection of views on the given sheet. Since I know the view name the dimension is associated to and all the view names on a given sheet, relating the dimension to the sheet is fairly easy. In fact, there are several viable options to relate the dimension and the sheet given the information we have. The solution I chose makes use of a dictionary object.

So what’s a dictionary object?

A physical dictionary is a large collection of pairs of items (words - definitions). If you wanted to recreate a physical dictionary in code, you could utilize a large 2 dimensional array.

Dim myVirtualDictionary(1,1)
myVirtualDictionary(0,0) = "aardvark"
myVirtualDictionary(0,1) = "medium-sized, burrowing, nocturnal mammal native to Africa"
myVirtualDictionary(1,0) = "zebra"
myVirtualDictionary(1,1) = "several species of African equids (horse family) united by their distinctive black and white stripes"

myVirtualDictionary array would look something like this:
 

 column 0column 1
row 0word 1definition 1
row 1word 2definition 2
.........
row nword ndefinition n

We could now write code that would search for a given word in column 1 and return the definition from column 2. The dictionary object is a specialized 2 dimensional array that does the searching for you. The dictionary object holds key-item pairs. Each key in the dictionary must be unique, but the item value can repeat if needed. Each word in the dictionary would be a key, and the definition would be the item. Continuing with the virtual dictionary example, the dictionary object could hold the keys “rabbit” and “bunny” with the exact same definition for each, but it could not hold two keys each with the value of “bunny”.

Using a dictionary object to manage our virtual dictionary instead of a generic 2 dimensional array would look like this:

Dim myDictionaryObject As New Collections.Generic.Dictionary(Of String, String)
myDictionaryObject.Add("aardvark", "medium-sized, burrowing, nocturnal mammal native to Africa")
myDictionaryObject.Add("zebra", "several species of African equids (horse family) united by their distinctive black and white stripes")
 
'retrieve a definition
Dim aardvarkDefinition as String
aardvarkDefinition = myDictionaryObject("aardvark")

Note: you can import the System.Collections.Generic namespace to minimize the amount of typing necessary when declaring a dictionary object. Since my journal only uses one dictionary object, I didn’t bother to do so.

Since we know each view name in the NX file will be unique, we will use those as the dictionary key values. We loop over each drawing sheet and get the collection of views on each sheet; the view is added to the dictionary as the key value and the sheet is set as the item value.

For Each sheet As Drawings.DrawingSheet In workPart.DrawingSheets
    viewList = sheet.GetDraftingViews
    For Each myView As View In viewList
           dictionary.Add(myView.Name, sheet.Name)
    Next
Next

Now when we want to know the sheet that a given view is on we can ask the dictionary object:

sheetName = dictionary(view.name)

That pretty much wraps it up for this time. I hope your future searches in the help file are more productive. Go forth and make good use of the Dictionary object! And who knows maybe you’ll even find a good use for the example journal.

As always, questions, comments, or suggestions for future articles can be sent to info@nxjournaling.com


==============================


Point3d And Point Objects

The point is a fundamental object for 3D modeling. It is a simple object, so will serve well for learning purposes; but they are also useful because they are often required when creating more complex entities (lines, arcs, splines, etc).
Even more fundamental than a point object is the Point3d structure, the NXOpen representation of a 3D coordinate. The Point3d is a three element array of doubles which provides the programmer (that’s us) a convenient way to access the X, Y, and Z coordinate values along with a few useful methods. A Point3d is simply variable data in your journal; by itself it does not create a visible point object. It does not appear in the NX graphics window or as an object anywhere in the interactive display, for that matter.
The Point3d structure provides a constructor which allows you to assign values at the time the variable is declared.

'create Point3d using the constructor
Dim myPt1 As New Point3d(0, 0, 0)

Alternatively, you can declare the variable and assign values later.
'create Point3d, then assign values
Dim myPt2 As Point3d
myPt2.X = 0
myPt2.Y = 0
myPt2.Z = 0


Like many NXOpen objects, the Point3d structure has a .ToString method. This method will return a string representation of the object. In the case of a Point3d, it will return labeled coordinate information.
The .Equals method allows you to test if one object equals another; a boolean value of True or False is returned.
The .GetType method will return the type of the object it is used on. This is useful when you are cycling through objects in the file looking for a particular type. If you use .GetType.ToString() on a Point3d variable, the string value “NXOpen.Point3d” will be returned.
The .X, .Y, and .Z members can be used to get or set the respective coordinate data. In the myPt2 example above, the XYZ members were used to assign values to the Point3d. We can assign new values in the same way.

myPt2.X = 2.71
myPt2.Y = 3.14
myPt2.Z = myPt2.Y * 2


Once a value is assigned, it can be used just like any other variable. You will notice the Z value above uses the Y value in its assignment.
Finally, the Point3d also provides the .GetHashCode() method. Hash codes are beyond the scope of this tutorial; but if you are interested, Wikipedia has a decent introductory article.

There is also a Point2d structure with all the methods and members of the Point3d structure, with the exception of the .Z member. As you may guess, this structure comes in handy for dealing with coordinates on the X-Y plane.

Below is a very short journal illustrating the use of the Point3d structure.

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01a
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim rnd1 As New Random()
       Dim lw As ListingWindow = theSession.ListingWindow
 
       lw.Open()
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
       'create Point3d, then assign values
       Dim myPt2 As Point3d
       myPt2.X = 0
       myPt2.Y = 0
       myPt2.Z = 0
 
       'output point information
       lw.WriteLine("myPt1: " & myPt1.ToString)
       lw.WriteLine("myPt2: " & myPt2.ToString)
       lw.WriteLine("myPt1 = myPt2 : " & myPt1.Equals(myPt2).ToString)
       lw.WriteLine("myPt1.GetType.ToString: " & myPt1.GetType.ToString)
       lw.WriteLine("")
 
       'change values of myPt1 using random numbers
       myPt1.X = rnd1.Next(-10, 10) + rnd1.NextDouble
       myPt1.Y = rnd1.Next(-10, 10) + rnd1.NextDouble
       myPt1.Z = rnd1.Next(-10, 10) + rnd1.NextDouble
 
       'output new point information
       lw.WriteLine("myPt1: " & myPt1.ToString)
       lw.WriteLine("myPt2: " & myPt2.ToString)
       lw.WriteLine("myPt1 = myPt2 : " & myPt1.Equals(myPt2).ToString)
       lw.WriteLine("")
 
       'show values of myPt1 rounded to 3 decimal places
       lw.WriteLine(myPt1.X.ToString("[X=0.###;[X=-0.###") & "," & myPt1.Y.ToString("Y=0.###;Y=-0.###") & "," & myPt1.Z.ToString("Z=0.###];Z=-0.###]"))
       lw.Close()
 
 
    End Sub
 
 
    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

The NXOpen .NET API help file lists no less than 20 ways to create a point, considering that most of these methods can be used for creating a dumb point or an associative point feature, you’ll see that there is simply too much ground to cover in one tutorial. We’ll look at a basic method for creating dumb points and build on that in later tutorial(s).


Creating A Dumb Point With CreatePoint(Point3d)

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01b
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim rnd1 As New Random()
       Dim lw As ListingWindow = theSession.ListingWindow
       Dim newPt(1000) As Point
       Dim max As Integer
 
       lw.Open()
       'lw.WriteLine(workPart.PartUnits.ToString)
       If workPart.PartUnits = Part.Units.Inches Then
             max = 10
       Else    'metric file
             max = 250
       End If
 
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
 
       'lw.WriteLine("max = " & max)
 
       For i As Integer = 0 To 1000
             'change values of myPt1 using random numbers
             myPt1.X = rnd1.Next(-max, max) + rnd1.NextDouble
             myPt1.Y = rnd1.Next(-max, max) + rnd1.NextDouble
             myPt1.Z = rnd1.Next(-max, max) + rnd1.NextDouble
             newPt(i) = workPart.Points.CreatePoint(myPt1)
             newPt(i).SetVisibility(SmartObject.VisibilityOption.Visible)
             newPt(i).Layer = rnd1.Next(1, 257)
       Next
 
       lw.Close()
 
    End Sub
 
 
    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

The journal starts off by declaring some variables; all of the code is contained within Sub Main() so there are no module level variables declared. The notable declarations here are:
 Dim rnd1 As New Random()
Random is a class in the .NET framework used to generate random numbers. We'll use it to generate point coordinate data.

 Dim newPt(1000) As Point
Point is a class in the NXOpen API used to represent point objects.

The next section of code checks the part units (inch or millimeter) and sets the value of a variable named max accordingly. The max variable will be used to define a bounding cube for the points we generate.

The for loop creates the points. Coordinate data is generated by random numbers.
myPt1.X = rnd1.Next(-max, max) + rnd1.NextDouble
rnd1.Next(-max, max) returns a random integer in the range -max (inclusive) to max (exclusive); rnd1.NextDouble returns a random number between 0 (inclusive) and 1 (exclusive). The X, Y, and Z coordinate data for each point can then range anywhere within -max to max.
Each part file has a PointCollection object associated to it. It is through this object that we can query existing points and create new ones. CreatePoint(point3d) is a method of the PointCollection object, the following line of code accesses the point collection of the work part and creates the point... mostly
newPt(i) = workPart.Points.CreatePoint(myPt1)
The default visibility option for new points is "Invisible", not what we need for this journal. So...
newPt(i).SetVisibility(SmartObject.VisibilityOption.Visible)
makes our new point visible.
The following line assigns the point object to a random layer (1-256). If you don't specify properties such as color, layer, lineweight (not very useful for points), etc. they will be assigned the current default. All the points will be created in your current color (or default color for points if you have one specified) and if you comment out the layer assignment all the points will be created on your work layer. I assign the point to a random layer so that in the next journal we can see how to access and modify property values such as layer, color, etc.

Working With Existing Points

The following journal demonstrates how to work with existing points in a file. Run the previous journal that creates points before running this one.

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01c
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim lw As ListingWindow = theSession.ListingWindow
       Dim max As Double
       Dim myPoints() As Point
       Dim pointsOnLayer(256) As Integer
       Dim maxPoints As Integer
       Dim maxLayer As Integer
       Dim ptXYZ As Point3d
 
       lw.Open()
       'lw.WriteLine(workPart.PartUnits.ToString)
       If workPart.PartUnits = Part.Units.Inches Then
               max = 10
       Else    'metric file
               max = 250
       End If
 
       myPoints = workPart.Points.ToArray
       For Each tempPoint As Point In myPoints
               'if the layer number is odd, move point to next even numbered layer
               If tempPoint.Layer Mod 2 > 0 Then
               tempPoint.Layer += 1
               End If
 
               'keep track of how many points are on each layer
               pointsOnLayer(tempPoint.Layer) += 1
 
               'keep track of which layer has the most point objects
               If pointsOnLayer(tempPoint.Layer) > maxPoints Then
               maxPoints = pointsOnLayer(tempPoint.Layer)
               maxLayer = tempPoint.Layer
               End If
 
       Next
 
       'move first 2 points to opposite corners of bounding cube
       ptXYZ = New Point3d(-max, -max, -max)
       myPoints(0).SetCoordinates(ptXYZ)
       myPoints(0).SetName("-max, -max, -max")
       myPoints(0).Color = 1
       myPoints(0).RedisplayObject()
 
       ptXYZ = New Point3d(max, max, max)
       myPoints(1).SetCoordinates(ptXYZ)
       myPoints(1).SetName("max, max, max")
       myPoints(1).Color = 1
       myPoints(1).RedisplayObject()
 
       'turn on name display for the work view
       workPart.Preferences.NamesBorderVisualization.ObjectNameDisplay = Preferences.PartVisualizationNamesBorders.NameDisplay.WorkView
 
       'move point to closest face of bounding cube
       For i As Integer = 2 To myPoints.GetUpperBound(0)
           ptXYZ = myPoints(i).Coordinates
           If Math.Abs(ptXYZ.X) > Math.Abs(ptXYZ.Y) Then
               'x is bigger
               If Math.Abs(ptXYZ.X) > Math.Abs(ptXYZ.Z) Then
                   'x is bigger
                   myPoints(i).SetCoordinates(New Point3d(Math.Sign(ptXYZ.X) * max, ptXYZ.Y, ptXYZ.Z))
               Else
                   'z is bigger
                   myPoints(i).SetCoordinates(New Point3d(ptXYZ.X, ptXYZ.Y, Math.Sign(ptXYZ.Z) * max))
               End If
           Else
               'y is bigger
               If Math.Abs(ptXYZ.Y) > Math.Abs(ptXYZ.Z) Then
                   'y is bigger
                   myPoints(i).SetCoordinates(New Point3d(ptXYZ.X, Math.Sign(ptXYZ.Y) * max, ptXYZ.Z))
               Else
                   'z is bigger
                   myPoints(i).SetCoordinates(New Point3d(ptXYZ.X, ptXYZ.Y, Math.Sign(ptXYZ.Z) * max))
               End If
           End If
           myPoints(i).RedisplayObject()
       Next
 
       'report the number of points on each even numbered layer
       For i As Integer = 2 To 256 Step 2
           lw.WriteLine("Points on Layer: " & i & " = " & pointsOnLayer(i).ToString)
       Next
       lw.WriteLine("")
       'report which layer has the most point objects
       lw.WriteLine("Max point count = " & maxPoints & " on layer: " & maxLayer)
       lw.Close()
 
    End Sub
 
 
    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

At the start of this journal, an empty array of point objects was created. The line:
myPoints = workPart.Points.ToArray
converts the point collection of the work part to an array of Point objects and assigns it to our array variable. We now have access to all the point objects in the current work part.

The For loop checks each point in the array. If the point object is on an odd numbered layer; the journal increases the layer number by one, moving it to the next higher even numbered layer. After the layer check the journal increments an array variable that keeps track of how many points are on each layer and it increments an integer variable that keeps track of the layer number that holds the most point objects.

The next group of statements demonstrates how to change the coordinates of an existing point, set the name property, and change the color. The .RedisplayObject() method forces an update so the changes can be seen, otherwise the changes may not be apparent until the system (or user) refreshed the display.

The following statment turns on the name display in the work view so you can see which points we have assigned names to.

The next For loop moves the point to the closest face of the bounding cube. The .SetCoordinates() method is used to change the existing points and the absolute value (.Abs) and sign (.Sign) methods of the .NET Math class are used.

Finally, some information on the point objects is output to the listing window.

Conclusion

In this tutorial we learned about the Point3d structure and how it can be used to create point objects. Along the way we learned a little about the PointCollection and part units of the work part, .NET random numbers, and a couple of .NET Math functions (.Abs and .Sign). I hope you learned something that will be useful in your future journals. If you have any specific questions, find any bugs, or have any comments please email them to info@nxjournaling.com or leave them in the comments section below.


======================


Point3d And Point Objects: Part 2

Working with Points Part 2

Creating points by specifying exact coordinates is well and good, but often you don’t much care about the location in absolute space but rather you need it to be located relative to another piece of geometry: the midpoint of a line, the intersection of a curve and a plane, the center of an arc. You can go through the trouble of calculating these locations and creating points at the coordinates, or you can use the tools in the NXOpen API to help out.

Use your head

The NXOpen API contains a “SmartObject” class which provides a number of ways to create associations between objects. Scalars, Directions, and Offsets are some of the smart objects we will be using in this tutorial. The use of these objects will make your journal code more robust and will give you some insight as to how NX works its magic.

Here is an example of creating a point with a given offset from an existing point:
 

 

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim lw As ListingWindow = theSession.ListingWindow
 
       lw.Open()
 
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
 
       'create center point
       Dim ptObj As Point
       ptObj = workPart.Points.CreatePoint(myPt1)
       ptObj.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       Dim myVector As Vector3d
       'enter values for vector
       myVector.X = (1)
       myVector.Y = (2)
       myVector.Z = (3)
 
       'create offset
       Dim myOffset As Offset
       myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
 
       'create new point offset from first point
       Dim offsetPoint As Point
       offsetPoint = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
       offsetPoint.RemoveParameters()
       offsetPoint.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       lw.Close()
 
    End Sub
 
    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

 

We start off by creating a point using coordinates created with the point3d structure. Next a Vector3d structure is created and given some values. To the programmer, the Vector3d structure is practically identical to the Point3d structure, but there are internal differences and you will get an error if you pass a Point3d to a function that expects a Vector3d (or vice versa).

An offset defines a spatial relationship between two objects. Each part file contains a collection of offset objects, this collection has methods to create new offsets. The create method used above requires a Point3d, Vector3d, and an update option.

 

myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)

 

For the Point3d, we use the coordinates property of our existing point, which conveniently returns a Point3d structure. For this simple example, any point will do; it is the vector that is important. Even though we specify our existing point, there is no association created at this time; later, in the CreatePoint method, the association will be created between our existing point and the new point.

The update option gives you control over when the associated object updates. There are four choices for the update option (from the help file):

DontUpdateNo update occurs
WithinModelingUpdates within modeling (e.g. a feature)
AfterModelingUpdates after modeling (e.g. a dimension)
AfterParentBodyUpdates after the parent body

The next section of code creates our new point object. The new point uses the offset we created and references the existing point object; now there is an association between the new point and the existing point.

 

offsetPoint = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)

 

The next two lines removes the parameters of the new point and makes it visible.

 

       offsetPoint.RemoveParameters()
       offsetPoint.SetVisibility(SmartObject.VisibilityOption.Visible)

 

When we remove the parameters, the new point object becomes a regular dumb point. In essence, we have used an offset vector as an alternate way of specifying the point’s coordinates. This journal could be modified to allow the user to pick a point and create a new point with a given offset; you would not have to calculate the new coordinates and specify the new point with a Point3d structure.

Comment out the line of code that removes the parameters and run the journal again. There is a difference that is not immediately apparent. If you move the initial point (the one created at the origin) the new point will follow to maintain the given offset. If you try to move the offset point, NX will not allow you to select it. What we have created is neither a dumb point object nor an associative point feature, I’ll call it a smart point object. I know of no way to create smart point objects in interactive NX (if you know a way, please email me at info@nxjournaling.com to let me know). Since the smart point objects may confuse the average user, I suggest either removing the parameters to create a normal point object or adding some code to turn it into an associative point feature.

Turning this point into an associative point feature will require a bit more up front work. A feature will need to be fully parameterized so the user can edit any aspect; our previous smart point object has parameters, but they are internal to NX and the interactive user has no way to edit them directly. The following journal is a rewrite of our previous code to allow the point to be turned into a full-fledged associative point feature.
 

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim lw As ListingWindow = theSession.ListingWindow
 
       lw.Open()
 
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
 
       'create point object at origin
       Dim ptObj As Point
       ptObj = workPart.Points.CreatePoint(myPt1)
       ptObj.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       Dim unit1 As Unit = CType(workPart.UnitCollection.FindObject("Inch"), Unit)
 
       'create expression for X offset, NX will generate the next available expression name
       Dim Xexp As Expression = workPart.Expressions.CreateSystemExpressionWithUnits("1", unit1)
 
       Dim expName As String
       'return name of system generated expression
       expName = Xexp.Name
       workPart.Expressions.SystemRename(Xexp, expName & "_X")
 
       'create Y and Z expressions for the Y and Z offset distances
       Dim Yexp As Expression = workPart.Expressions.CreateWithUnits(expName & "_Y=2", unit1)
       Dim Zexp As Expression = workPart.Expressions.CreateWithUnits(expName & "_Z=3", unit1)
 
       'create scalars using previously created expressions
       Dim Xscalar As Scalar
       Dim Yscalar As Scalar
       Dim Zscalar As Scalar
       Xscalar = workPart.Scalars.CreateScalarExpression(Xexp, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
       Yscalar = workPart.Scalars.CreateScalarExpression(Yexp, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
       Zscalar = workPart.Scalars.CreateScalarExpression(Zexp, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
       'create rectangular offset using previously created scalars
       Dim myOffset As Offset
       myOffset = workPart.Offsets.CreateOffsetRectangular(Xscalar, Yscalar, Zscalar, SmartObject.UpdateOption.WithinModeling)
 
       'create new point offset from first point
       Dim offsetPoint As Point
       offsetPoint = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
       offsetPoint.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       'code to make the smart point object into an associative point feature
       Dim nullFeatures_Feature As Features.Feature = Nothing
 
       Dim pointFeatureBuilder1 As Features.PointFeatureBuilder
       pointFeatureBuilder1 = workPart.BaseFeatures.CreatePointFeatureBuilder(nullFeatures_Feature)
 
       pointFeatureBuilder1.Point = offsetPoint
 
       'myPointFeature will be a reference to your new point feature
       Dim myPointFeature As Features.Feature
       myPointFeature = pointFeatureBuilder1.Commit()
 
       pointFeatureBuilder1.Destroy()
 
       myPointFeature.SetName("SPOT")
 
       'write out some feature properties to the information window
       lw.WriteLine("FeatureType: " & myPointFeature.FeatureType.ToString)
       lw.WriteLine("FeatureName: " & myPointFeature.GetFeatureName)
       lw.WriteLine("Name: " & myPointFeature.Name)
 
       lw.Close()
 
    End Sub
 
    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

 

We start out by creating a “dumb” point object at the origin using the tried and true method of specifying its coordinates with the Point3d structure. Next up we create a unit object and specify Inches as the unit of length. The Xexp expression is created as a system expression. This means the system will generate a usable name for our expression. If you run this journal in a brand new file, it will most likely generate the familiar “p0” name. Some interactive commands that create X, Y, and Z components generate one base expression name and append “_X”, “_Y”, and “_Z” respectively. We’ll emulate this behavior. We use the variable expName to store the system generated expression name. The Xexp expression is renamed to add the “_X” to the name, then the Y and Z expressions are created accordingly.

You may remember from physics class that there are two basic types of measurements: vector and scalar. Vectors represent a magnitude and direction, but scalars represent only a magnitude. 55 miles per hour, 9 meters, and 18 kilograms are all examples of scalar measurements. Each NX part file maintains a collection of scalar objects to help maintain relationships among geometric objects. In our journal, scalar objects are created that reference our expressions, then the offset object is created that uses our parameterized scalars. The offset point is created with the same line of code as before, only this time there are user editable expressions that ultimately drive the location of the point.

Build to Order

The NXOpen API provides a number of specialty “builder” objects to assist in editing existing objects or creating new ones. The overall strategy for using a builder object is fairly simple:

  • create the builder object of interest
  • pass in a reference to the object you want to edit, or a null reference if you want to create a new object
  • use the builder’s properties and methods to define/redefine the object
  • if all goes well, commit to the changes; which creates the object
  • destroy the builder object to free up the memory it used

The last section of the previous code listing turns our newly created point into a point feature. To do this we use a “pointFeatureBuilder” provided by the NXOpen API. Since we will be creating a new point feature, as opposed to editing an existing point feature, we create a null feature and pass that in to the builder object. We pass in our newly created point to the builder’s point property, commit to the changes, and destroy the builder object. The builder’s commit method returns the point feature; we save this reference in the “myPointFeature” variable. To verify our reference to the point feature we give it a name, then output some of its properties to the listing window.

It’s Alive!!

We now have a fully associative point feature that behaves as if you had created it in an interactive session. If you right click on the feature, the point creation dialog box will appear where you can edit the offset values or the object it is associated to. If you move the parent point (the “dumb” point at the origin), the point feature will follow.

Additional examples
The following examples demonstrate a few more ways of creating points. To help keep the code listings short, dumb points are created.

Create a point at a certain percentage of the distance between 2 existing points:
 

Option Strict Off
Imports System
Imports NXOpen
 
Module create_point_02
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim lw As ListingWindow = theSession.ListingWindow
       Dim rnd1 As New Random()
 
       lw.Open()
 
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
       Dim myPt2 As New Point3d(rnd1.Next(-10, 10), rnd1.Next(-10, 10), rnd1.Next(-10, 10))
 
       'create point object at origin
       Dim ptObj As Point
       ptObj = workPart.Points.CreatePoint(myPt1)
       ptObj.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       'create random point
       Dim ptObj2 As Point
       ptObj2 = workPart.Points.CreatePoint(myPt2)
       ptObj2.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       'create scalar representing the percentage of the distance between 2 points
       Dim distanceScalar As Scalar
       distanceScalar = workPart.Scalars.CreateScalar(0.3, Scalar.DimensionalityType.None, SmartObject.UpdateOption.WithinModeling)
 
       'create new point offset from first point
       Dim betweenPoint As Point
       betweenPoint = workPart.Points.CreatePoint(ptObj, ptObj2, distanceScalar, SmartObject.UpdateOption.WithinModeling)
       betweenPoint.RemoveParameters()
       betweenPoint.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       lw.Close()
 
    End Sub
 
 
    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

 

The following code demonstrates spherical offset and offsets along a vector.
 

Option Strict Off
Imports System
Imports NXOpen
 
Module Points_01
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
       Dim rnd1 As New Random()
       Dim lw As ListingWindow = theSession.ListingWindow
       Dim newPt(1000) As Point
       Dim myOffset As Offset
       Dim scalarRad As Scalar
       Dim scalarAngle1 As Scalar
       Dim scalarAngle2 As Scalar
       Dim max As Integer
 
       lw.Open()
       'lw.WriteLine(workPart.PartUnits.ToString)
       If workPart.PartUnits = Part.Units.Inches Then
           max = 10
       Else    'metric file
           max = 250
       End If
 
       'create Point3d using the constructor
       Dim myPt1 As New Point3d(0, 0, 0)
       'create center point
       Dim ptObj As Point
       ptObj = workPart.Points.CreatePoint(myPt1)
       ptObj.Color = 1
       ptObj.SetVisibility(SmartObject.VisibilityOption.Visible)
 
       scalarRad = workPart.Scalars.CreateScalar(max, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.DontUpdate)
 
       For i As Integer = 0 To 1000
           scalarAngle1 = workPart.Scalars.CreateScalar(rnd1.Next(0, 180) * (Math.PI / 180), Scalar.DimensionalityType.Angle, SmartObject.UpdateOption.DontUpdate)
           scalarAngle2 = workPart.Scalars.CreateScalar(rnd1.Next(0, 360) * (Math.PI / 180), Scalar.DimensionalityType.Angle, SmartObject.UpdateOption.DontUpdate)
           myOffset = workPart.Offsets.CreateOffsetSpherical(scalarRad, scalarAngle1, scalarAngle2, SmartObject.UpdateOption.AfterParentBody)
 
           newPt(i) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.AfterParentBody)
           newPt(i).RemoveParameters()
           newPt(i).SetVisibility(SmartObject.VisibilityOption.Visible)
       Next
 
       Dim myVector As Vector3d = New Vector3d(0, 0, 0)
       Dim ptXPos(9) As Point
       Dim ptXNeg(9) As Point
       Dim ptYPos(9) As Point
       Dim ptYNeg(9) As Point
       Dim ptZPos(9) As Point
       Dim ptZNeg(9) As Point
 
       For i As Integer = 1 To 10
           ' +X points
           myVector.X = (i / 10) * max
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptXPos(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptXPos(i - 1).Color = 123
           ptXPos(i - 1).RemoveParameters()
           ptXPos(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           ' -X points
           myVector.X = -myVector.X
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptXNeg(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptXNeg(i - 1).Color = 123
           ptXNeg(i - 1).RemoveParameters()
           ptXNeg(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           myVector.X = 0
           ' +Y points
           myVector.Y = (i / 10) * max
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptYPos(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptYPos(i - 1).Color = 123
           ptYPos(i - 1).RemoveParameters()
           ptYPos(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           ' -Y points
           myVector.Y = -myVector.Y
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptYNeg(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptYNeg(i - 1).Color = 123
           ptYNeg(i - 1).RemoveParameters()
           ptYNeg(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           myVector.Y = 0
           ' +Z points
           myVector.Z = (i / 10) * max
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptZPos(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptZPos(i - 1).Color = 123
           ptZPos(i - 1).RemoveParameters()
           ptZPos(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           ' -Z points
           myVector.Z = -myVector.Z
           myOffset = workPart.Offsets.CreateOffset(ptObj.Coordinates, myVector, SmartObject.UpdateOption.WithinModeling)
           ptZNeg(i - 1) = workPart.Points.CreatePoint(myOffset, ptObj, SmartObject.UpdateOption.WithinModeling)
           ptZNeg(i - 1).Color = 123
           ptZNeg(i - 1).RemoveParameters()
           ptZNeg(i - 1).SetVisibility(SmartObject.VisibilityOption.Visible)
           myVector.Z = 0
 
       Next
 
 
       lw.Close()
 
    End Sub
 
    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

 

The code in its original form creates a full sphere of points, try changing the rnd1.Next(0, 180) and rnd1.Next(0, 360) values in the following lines to get a hemisphere or other portion of a sphere.
 

           scalarAngle1 = workPart.Scalars.CreateScalar(rnd1.Next(0, 180) * (Math.PI / 180), Scalar.DimensionalityType.Angle, SmartObject.UpdateOption.DontUpdate)
           scalarAngle2 = workPart.Scalars.CreateScalar(rnd1.Next(0, 360) * (Math.PI / 180), Scalar.DimensionalityType.Angle, SmartObject.UpdateOption.DontUpdate)

 

This last example creates a point object at the center of a selected arc or ellipse. Creating the point takes only a few lines of code, most of this journal is taken up by the selection function. Before running this journal, create some arcs and/or ellipses in your part file.
 

Option Strict Off
Imports System
Imports NXOpen
Imports NXOpen.UF
 
Module create_point_03
 
    Sub Main()
 
       Dim theSession As Session = Session.GetSession()
       Dim workPart As Part = theSession.Parts.Work
 
       Dim myCurve As IBaseCurve
 
       Do While select_a_curve(myCurve) = Selection.Response.Ok
 
           Dim myPoint As Point
 
           myPoint = workPart.Points.CreatePoint(myCurve, SmartObject.UpdateOption.WithinModeling)
           myPoint.SetVisibility(SmartObject.VisibilityOption.Visible)
           myPoint.RemoveParameters()
       Loop
 
    End Sub
 
    Function select_a_curve(ByRef curve As NXObject)
 
       Dim ui As UI = ui.GetUI()
       Dim message As String = "Select a curve"
       Dim title As String = "Selection"
       Dim selectionMask(1) As Selection.MaskTriple
       With selectionMask(0)
           .Type = UFConstants.UF_circle_type
           .Subtype = 0
           .SolidBodySubtype = 0
       End With
       With selectionMask(1)
           .Type = UFConstants.UF_conic_type
           .Subtype = 0
           .SolidBodySubtype = 0
       End With
       Dim cursor As Point3d = Nothing
 
       Dim resp As Selection.Response = _
           ui.SelectionManager.SelectObject(message, title, _
               Selection.SelectionScope.WorkPart, _
               Selection.SelectionAction.ClearAndEnableSpecific, _
               False, False, selectionMask, curve, cursor)
 
       If resp = Selection.Response.ObjectSelected Or _
          resp = Selection.Response.ObjectSelectedByName Then
           Return Selection.Response.Ok
       Else
           Return Selection.Response.Cancel
       End If
 
    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

 

If you have made it this far, I thank you for your perseverance; there was a lot of code this time around. I hope you have learned something useful. If you have any specific questions or comments, please post below!




===============




Creating Lines, Part 1

Creating Unassociative Lines

 

If you need to create some unassociative lines (think old basic curves), you are in luck; there are 2 quick and easy methods to accomplish this. To create these lines you can use the curve collection's CreateLine method which has two variations: you can pass it two Point3d variables or two Point objects. A quick example of each method is below:

 

'NXJournaling
'CreateLine example
 
Option Strict Off
Imports System
Imports NXOpen
 
Module Module1
 
    Sub Main()
 
        Dim theSession As Session = Session.GetSession()
        Dim workPart As Part = theSession.Parts.Work
 
        Dim markId1 As Session.UndoMarkId
        markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "NXJournaling.com")
 
        'create line given 2 point3d variables
        Dim ptStart As Point3d = New Point3d(0, 0, 0)
        Dim ptEnd As Point3d = New Point3d(-4, 5, -6)
 
        Dim line1 As Line
        line1 = workPart.Curves.CreateLine(ptStart, ptEnd)
 
 
        'create line given 2 point objects
        Dim pt1 As New Point3d(5, 5, 5)
        Dim pt2 As New Point3d(-5, -5, -5)
 
        'create point objects
        Dim ptObj1 As Point
        ptObj1 = workPart.Points.CreatePoint(pt1)
        ptObj1.SetVisibility(SmartObject.VisibilityOption.Visible)
 
        Dim ptObj2 As Point
        ptObj2 = workPart.Points.CreatePoint(pt2)
        ptObj2.SetVisibility(SmartObject.VisibilityOption.Visible)
 
        Dim line2 As Line
        line2 = workPart.Curves.CreateLine(ptObj1, ptObj2)
        line2.SetVisibility(SmartObject.VisibilityOption.Visible)
        line2.RemoveParameters()
 
    End Sub
 
 
    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

 

Using the Point3d version of the method is super easy, simply pass in two Point3d variables to the CreateLine method representing the start point and end point of the line - and that's it, you are done.

Using the CreateLine method that takes two Point objects is only slightly more complicated; after you create the line you will need to make it visible and remove the parameters. If you do not remove the parameters, the resulting line will remain associative to the two point objects (comment out the line of code that removes the parameters to try it out, move one or both points and the line will follow). An associative line doesn't sound so bad until you realize there is no way to edit the associativities in interactive NX, nor does it show up in the feature tree as an associative line feature would. We'll look at creating associative line features in an upcoming tutorial.

 

Finally, here is some code that uses the CreateLine method and a recursive subroutine to create some interesting fractal trees.

'NXJournaling
'a journal that creates fractal trees using the CreateLine method and recursion
 
Option Strict Off
Imports System
Imports System.Math
Imports NXOpen
 
Module Module1
 
    Dim theSession As Session = Session.GetSession()
    Dim workPart As Part = theSession.Parts.Work
    Dim deg_to_rad As Double = Math.PI / 180
 
    Sub Main()
 
        Dim markId1 As Session.UndoMarkId
        markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "NXJournaling.com")
 
        Dim depth As Integer = 9
        Dim ptStart As Point3d = New Point3d(0, 0, 0)
        DrawTree(ptStart, 90, depth)
 
    End Sub
 
    Sub DrawTree(ByVal startPoint As Point3d, ByVal angle As Double, ByVal depth As Integer)
 
        If depth = 0 Then
            Exit Sub
        End If
 
        Dim ptEnd As Point3d
        ptEnd.X = startPoint.X + Math.Cos(deg_to_rad * angle) * depth * 10
        ptEnd.Y = startPoint.Y + Math.Sin(deg_to_rad * angle) * depth * 10
        ptEnd.Z = 0
        workPart.Curves.CreateLine(startPoint, ptEnd)
        DrawTree(ptEnd, angle - 20, depth - 1)
        DrawTree(ptEnd, angle + 20, depth - 1)
 
 
    End Sub
 
    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

Comments

What is the " Public Function GetUnloadOption " for?

Carlo Tony Daristotile

The function "GetUnloadOption" is used in compiled code to specify when/if the code gets unloaded from memory. Leaving your code in memory can speed up subsequent runs, but these options only work for compiled journals; if you run an uncompiled journal it is unloaded by default when it finishes executing.

You can find a little more discussion in http://nxjournaling.com/?q=comment/157#comment-157

'NXJournaling
'a journal that creates fractal trees using the CreateLine method and recursion

Option Strict Off
Imports System
Imports System.Math
Imports NXOpen

Module Module1

Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
Dim deg_to_rad As Double = Math.PI / 180

Sub Main()

Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "NXJournaling.com")

Dim depth As Integer = 10
Dim ptStart As Point3d = New Point3d(0, 0, 0)
Dim ptEnd As Point3d = New Point3d(10, 10, 0)
DrawTree(ptStart, ptEnd, depth)

End Sub

Sub DrawTree(ByVal startPoint As Point3d, ByVal endPoint As Point3d, ByVal depth As Integer)

If depth = 0 Then
Exit Sub
End If

'find End Points of 3 lines , one straight, one left, one right
Dim ptEndS As Point3d
Dim ptEndL As Point3d
Dim ptEndR As Point3d

'find End Point of straight line
ptEndS.X = endPoint.X + (endPoint.X - startPoint.X)*0.5
ptEndS.Y = endPoint.Y + (endPoint.Y - startPoint.Y)*0.5
ptEndS.Z = endPoint.Z + (endPoint.Z - startPoint.Z)*0.5

'find End Point of right line
ptEndL.X = endPoint.X - (endPoint.X - startPoint.X)*0.5
ptEndL.Y = endPoint.Y + (endPoint.Y - startPoint.Y)*0.5
ptEndL.Z = endPoint.Z + 0

'find End Point of left line
ptEndR.X = endPoint.X + (endPoint.X - startPoint.X)*0.5
ptEndR.Y = endPoint.Y - (endPoint.Y - startPoint.Y)*0.5
ptEndR.Z = endPoint.Z + 0

'draw 3 lines , one straight, one left, one right
workPart.Curves.CreateLine(endPoint , ptEndS )
workPart.Curves.CreateLine(endPoint , ptEndL )
workPart.Curves.CreateLine(endPoint , ptEndR )

'make 3 new lines , one straight, one left, one right, for each line just made
DrawTree(endPoint , ptEndS , depth-1)
DrawTree(endPoint , ptEndL , depth-1)
DrawTree(endPoint , ptEndR , depth-1)

End Sub

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

Carlo Tony Daristotile

Very cool design.
Thanks for sharing.

'MAKE THE FOLLOWING CHANGE TO MY CODE ABOVE
'IT WILL LOOK LIKE A MAPLE LEAF

'find End Point of right line
ptEndL.X = endPoint.X - (endPoint.X - startPoint.X)*0.45
ptEndL.Y = endPoint.Y + (endPoint.Y - startPoint.Y)*0.45
ptEndL.Z = endPoint.Z + 0

'find End Point of left line
ptEndR.X = endPoint.X + (endPoint.X - startPoint.X)*0.45
ptEndR.Y = endPoint.Y - (endPoint.Y - startPoint.Y)*0.45
ptEndR.Z = endPoint.Z + 0

Carlo Tony Daristotile

HOW TO ADD "ARC OR LINE OR POINT" TO THE MODEL REFERENCE SET ?

Carlo Tony Daristotile

I'd suggest recording a journal while performing the operation desired. This operation should be fully supported by the journal recorder and will result in example code that you can change to your needs.

Hello,
The recorded macro has a line , but how do i guarantee this is always the reference set MODEL("PART")

Dim referenceSet1 As ReferenceSet = CType(workPart.FindObject("HANDLE R-3322"), ReferenceSet)

Carlo Tony Daristotile

To find a part's "model" reference set, have a look here:
http://www.nxjournaling.com/?q=content/model-reference-set

I am not able to delete ptObj1 and ptObj2 after the line is created. I tried the following:

        Dim line2 As Line
        line2 = workPart.Curves.CreateLine(ptObj1, ptObj2)
        line2.SetVisibility(SmartObject.VisibilityOption.Visible)
         line2.RemoveParameters()
 
	theSession.UpdateManager.AddToDeleteList(ptObj1)
	theSession.UpdateManager.AddToDeleteList(ptObj2)

and

        Dim line2 As Line
        line2 = workPart.Curves.CreateLine(ptObj1, ptObj2)
        line2.SetVisibility(SmartObject.VisibilityOption.Visible)
 
	theSession.UpdateManager.AddToDeleteList(ptObj1)
	theSession.UpdateManager.AddToDeleteList(ptObj2)

Both snippets can be run but in the NX graphic window, I can still see those two points. I ask this because I have a project wih a grid of points that are to be projected down to many faces. Only the projected points are meaningful to me. So I want to delete the original and auxillary grid points.

Don't forget to call the .DoUpdate method after adding the points to the delete list. Below is a snippet from a recorded journal.

theSession.UpdateManager.ClearErrorList()
 
Dim markId2 As Session.UndoMarkId
markId2 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Delete")
 
Dim objects1(0) As NXObject
Dim point1 As Point = CType(workPart.Points.FindObject("ENTITY 2 1 1"), Point)
 
objects1(0) = point1
Dim nErrs1 As Integer
nErrs1 = theSession.UpdateManager.AddToDeleteList(objects1)
 
Dim nErrs2 As Integer
nErrs2 = theSession.UpdateManager.DoUpdate(markId2)

hello,
Where can I find commands and their examples:
- workPart.Curves.CreateLine
- workPart.Points.CreatePoint
- workPart.Scalars.CreateScalar
etc..

Kien

While learning and writing NXOpen code, I have found 3 resources to be very valuable:
1. other people's code
2. the journal recorder
3. the NX help files

You can find many code examples at the GTAC solution centerthe Siemens forum, this site, and eng-tips. If you can find something close to what you want, it is a great starting point.

If an NX command that you want to use is supported by the journal recorder, it is a great way to learn the syntax of the command. The journal recorder often returns a lot of code, some of which you won't need. What you need will be in there, but it can be a bit tedious separating what you need from what you do not.

Once you know the objects and methods that you need to accomplish your task, the help file is a great way to learn what other methods are available to tweak the functionality, if needed. The help file lists all the objects and methods available to you, but has very few code examples.

None of the methods above provide much in the way of explanation (sometimes the forums do, if you are lucky). This site was created in part to help fill that gap. I've been looking for ideas for new articles; if you have some general questions about programming and/or the NXOpen API, please let me know.


Creating Lines, Part 2

Associative Line Features

In this tutorial, we'll look at creating some associative lines. These will be fully associative to the defining geometry and will show up in the feature tree as line features. 

 

The first journal creates a line with an associative start and end point. This is equivalent to the interactive operation of creating a line with the associative option checked and typing in start and end point coordinates.

 

'NXJournaling.com
'create associative line example journal
'creates line from (1,2,0) to (4,5,0)
'tested on NX 7.5 and 8
 
 
Option Strict Off
Imports System
Imports NXOpen
 
Module Module1
 
    Sub Main()
 
        Dim theSession As Session = Session.GetSession()
        Dim workPart As Part = theSession.Parts.Work
        Dim lw As ListingWindow = theSession.ListingWindow
        lw.Open()
 
        Dim markId1 As Session.UndoMarkId
        markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "line journal")
 
        Dim nullFeatures_AssociativeLine As Features.AssociativeLine = Nothing
 
        Dim associativeLineBuilder1 As Features.AssociativeLineBuilder
        associativeLineBuilder1 = workPart.BaseFeatures.CreateAssociativeLineBuilder(nullFeatures_AssociativeLine)
 
        Dim unit1 As Unit
        unit1 = associativeLineBuilder1.Limits.StartLimit.Distance.Units
 
        associativeLineBuilder1.StartPointOptions = Features.AssociativeLineBuilder.StartOption.Point
        'start point X coordinate
        Dim expressionX1 As Expression
        expressionX1 = workPart.Expressions.CreateSystemExpressionWithUnits("1", unit1)
 
        Dim scalar1 As Scalar
        scalar1 = workPart.Scalars.CreateScalarExpression(expressionX1, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        'start point Y coordinate
        Dim expressionY1 As Expression
        expressionY1 = workPart.Expressions.CreateSystemExpressionWithUnits("2", unit1)
 
        Dim scalar2 As Scalar
        scalar2 = workPart.Scalars.CreateScalarExpression(expressionY1, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        'start point Z coordinate
        Dim expressionZ1 As Expression
        expressionZ1 = workPart.Expressions.CreateSystemExpressionWithUnits("0", unit1)
 
        Dim scalar3 As Scalar
        scalar3 = workPart.Scalars.CreateScalarExpression(expressionZ1, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        Dim point1 As Point
        point1 = workPart.Points.CreatePoint(scalar1, scalar2, scalar3, SmartObject.UpdateOption.WithinModeling)
 
        associativeLineBuilder1.StartPoint.Value = point1
 
        associativeLineBuilder1.EndPointOptions = Features.AssociativeLineBuilder.EndOption.Point
 
        'end point X coordinate
        Dim expressionX2 As Expression
        expressionX2 = workPart.Expressions.CreateSystemExpressionWithUnits("4", unit1)
 
        Dim scalar4 As Scalar
        scalar4 = workPart.Scalars.CreateScalarExpression(expressionX2, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        'end point Y coordinate
        Dim expressionY2 As Expression
        expressionY2 = workPart.Expressions.CreateSystemExpressionWithUnits("5", unit1)
 
        Dim scalar5 As Scalar
        scalar5 = workPart.Scalars.CreateScalarExpression(expressionY2, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        'end point Z coordinate
        Dim expressionZ2 As Expression
        expressionZ2 = workPart.Expressions.CreateSystemExpressionWithUnits("0", unit1)
 
        Dim scalar6 As Scalar
        scalar6 = workPart.Scalars.CreateScalarExpression(expressionZ2, Scalar.DimensionalityType.Length, SmartObject.UpdateOption.WithinModeling)
 
        Dim point2 As Point
        point2 = workPart.Points.CreatePoint(scalar4, scalar5, scalar6, SmartObject.UpdateOption.WithinModeling)
 
        associativeLineBuilder1.EndPoint.Value = point2
 
        'code as output by the journal recorder
        'Dim nXObject1 As NXObject
        'nXObject1 = associativeLineBuilder1.Commit()
 
        'code specific to our new line object
        Dim myLineFeature As Features.AssociativeLine
        myLineFeature = associativeLineBuilder1.Commit
 
        associativeLineBuilder1.Destroy()
 
        'do something with the new line feature
        lw.WriteLine("line feature timestamp: " & myLineFeature.Timestamp)
 
        'get entities created by the feature
        Dim myEntities() As NXObject
        myEntities = myLineFeature.GetEntities()
 
        'we created one line, so it should be the first (and only) entity returned
        Dim myLine As Line
        myLine = myEntities(0)
 
        lw.WriteLine("line length: " & myLine.GetLength.ToString)
        lw.WriteLine("line start: " & myLine.StartPoint.ToString)
        lw.WriteLine("line end: " & myLine.EndPoint.ToString)
 
        lw.Close()
 
    End Sub
 
 
    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

 

 

This is largely a recorded journal that has been cleared of unnecessary code. It starts by creating theSession and workPart variables then creates an undo mark. The undo mark serves two purposes:

  1. a visible undo mark will show up in the undo list, allowing the user to undo the actions of the journal if necessary.
  2. should anything go horribly wrong in the statements following the undo mark, NX can restore itself to a known good state.

A unit variable is created that will reference the length units of the part the journal is run in; i.e. for a metric part the units will be mm, for an imperial part the units will be inch. The units variable will be used in the definition of the line start and end points.

 

An expression and scalar is created for each point coordinate. The expression will hold the coordinate value; in this journal they are simply integers but could be decimal numbers or equations involving other expressions. The expressions are created using the CreateSystemExpressionWithUnits method; there are several methods available when creating an expression, the system expression methods allow NX to manage the expression name. NX will choose a valid and available, if uninspired, name such as p402. If you want to give the expression a more meaningful name, or allow the user to input an expression name, you can rename an existing expression or use one of the creation methods that allow you to specify an expression name. If/when you or the user specifies an expression name, be sure to check that the name is currently unused and valid (some characters are illegal, check NX help for info on your version). The scalar is an internal object that will be used to associate the expression with the point object. When we create the scalar object we specify the expression we want it to be associated with, the dimensionality (which for this journal is length) and the update option. There are four update options available, within modeling means it will update right away, which is what we need to keep our line feature and any potential child features up to date. A point object is created for the line's start and end points. Like the scalar objects, these points are used internally in NX and will not show up in the feature tree or on the graphical display. The CreatePoint method used references the previously created scalar objects which allows the point locations to be driven by user editable expressions.

 

Once the points are created, all that is really left to do is assign those points to the associative line builder's start point and end point properties and calling the commit method. The Commit method returns the newly created object(s) from the builder. In the case of an AssociativeLineBuilder, an object of type Features.AssociativeLine is returned. The journal recorder creates an array of NXObject as a generic catch all. I have commented out these lines and instead created a more specific object to match the output. The associative line builder's Destroy method is then called to free up the memory used by this object. Then, to prove we have a reference to the line feature, its timestamp is output to the listing window. To get at the actual line object we created, we call the line feature's GetEntities() method; this method will return an array of type NXObject. We declare a variable of type Line and retrieve the first (and only) object from the myEntities array. To prove we have the line object we then use it to write some information out to the listing window.

 

Warning: DO NOT alter properties of this line object that would change its geometry (changing the start or end points, for instance). If you need to edit the associative line with a journal you should create an associative line builder object and pass it a reference to the line feature you wish to edit (a null reference creates a new line feature, a reference to an existing feature edits that feature).

 

Associative Tangent Lines

The next example journal will create two circles at random (the center points will fall within (-10,-10)-(10,10) and the radii will be between 1 and 4) then 2 associative tangent lines to create a "belt" like shape. Warning: it is possible that one circle will be created within the other; in this case the journal will error out because no tangent lines are possible. To keep the code short and readable, no checks are made for this error condition. If the code errors out, OK the dialog boxes, undo the operation and try running the journal again.

 

The associative line builder's start and end options will be set to Tangent, and the start and end values will be set to the created circles. Finally we will need to supply a start and end point. Don't worry, you do not have to calculate the exact start and end points that will result in the tangent line. The supplied points will help the NX solver determine which tangent line you want, similar to interactive NX - the side of the circle you pick determines which tangent line is created. For the first tangent line, we supply the arc center + the arc radius; and for the second tangent line we supply the arc center - the arc radius. After running the journal, move one or both of the circles to make sure the associative tangent lines update correctly.

 

The end of the journal is left as the journal recorder created it. If you need to refer to the line features or objects created modify it as in the first example journal.

 

'NXJournaling.com
'create associative tangent lines example
'this journal will create 2 circles at random (center point between (-10,-10) and (10,10), radius between 1 part unit and 4 part units)
'the journal will then create 2 associative lines tangent to these 2 circles
'*** may error out if one circle contains the other, there are no provisions in the code to check/handle this condition
'
'tested on NX 7.5 and 8
 
 
Option Strict Off
Imports System
Imports NXOpen
 
Module Module1
 
    Sub Main()
 
        Dim theSession As Session = Session.GetSession()
        Dim workPart As Part = theSession.Parts.Work
        'create random number generator
        Dim rnd1 As New Random()
 
        'set undo mark
        Dim markId1 As Session.UndoMarkId
        markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Journal")
 
        'create matrix that will be used when creating the circles (absolute coordinate system)
        Dim identity As Matrix3x3
        identity.Xx = 1
        identity.Xy = 0
        identity.Xz = 0
        identity.Yx = 0
        identity.Yy = 1
        identity.Yz = 0
        identity.Zx = 0
        identity.Zy = 0
        identity.Zz = 1
 
        Dim circleCoordinateSystem As NXMatrix = workPart.NXMatrices.Create(identity)
 
        'generate circle center points and radii
        Dim c1 As Point3d = New Point3d(rnd1.Next(-10, 10), rnd1.Next(-10, 10), 0)
        Dim c2 As Point3d = New Point3d(rnd1.Next(-10, 10), rnd1.Next(-10, 10), 0)
        Dim cr1 As Double = rnd1.Next(1, 3) + rnd1.NextDouble
        Dim cr2 As Double = rnd1.Next(1, 3) + rnd1.NextDouble
 
        'create the circles
        Dim arc1 As Arc = workPart.Curves.CreateArc(c1, circleCoordinateSystem, cr1, 0, 2 * Math.PI)
        Dim arc2 As Arc = workPart.Curves.CreateArc(c2, circleCoordinateSystem, cr2, 0, 2 * Math.PI)
 
        'create null feature, when passed to the associative line builder this will create a new line
        '  to edit an existing line, pass the existing line feature to the associative line builder
        Dim nullFeatures_AssociativeLine As Features.AssociativeLine = Nothing
 
        Dim associativeLineBuilder1 As Features.AssociativeLineBuilder
        associativeLineBuilder1 = workPart.BaseFeatures.CreateAssociativeLineBuilder(nullFeatures_AssociativeLine)
 
        'set options for first tangent line
        associativeLineBuilder1.StartPointOptions = Features.AssociativeLineBuilder.StartOption.Tangent
        associativeLineBuilder1.StartTangent.Value = arc1
 
        associativeLineBuilder1.EndPointOptions = Features.AssociativeLineBuilder.EndOption.Tangent
        associativeLineBuilder1.EndTangent.Value = arc2
 
        Dim nullView As View = Nothing
 
        'create a point near where we want the tangent line to start
        Dim point1 As Point3d = New Point3d(arc1.CenterPoint.X + arc1.Radius, arc1.CenterPoint.Y + arc1.Radius, 0.0)
        associativeLineBuilder1.StartTangent.SetValue(arc1, nullView, point1)
 
        'create a point near where we want the tangent line to end
        Dim point2 As Point3d = New Point3d(arc2.CenterPoint.X + arc2.Radius, arc2.CenterPoint.Y + arc2.Radius, 0.0)
        associativeLineBuilder1.EndTangent.SetValue(arc2, nullView, point2)
 
        'the objects created by the associative line builder, returned as generic NXObjects
        Dim nXObject1 As NXObject
        nXObject1 = associativeLineBuilder1.Commit()
 
        Dim objects1() As NXObject
        objects1 = associativeLineBuilder1.GetCommittedObjects()
 
        'set options for second tangent line
        associativeLineBuilder1 = workPart.BaseFeatures.CreateAssociativeLineBuilder(nullFeatures_AssociativeLine)
 
        associativeLineBuilder1.StartPointOptions = Features.AssociativeLineBuilder.StartOption.Tangent
        associativeLineBuilder1.StartTangent.Value = arc1
 
        associativeLineBuilder1.EndPointOptions = Features.AssociativeLineBuilder.EndOption.Tangent
        associativeLineBuilder1.EndTangent.Value = arc2
 
        'create a point near where we want the tangent line to start, notice sign change from first tangent line
        point1 = New Point3d(arc1.CenterPoint.X - arc1.Radius, arc1.CenterPoint.Y - arc1.Radius, 0.0)
        associativeLineBuilder1.StartTangent.SetValue(arc1, nullView, point1)
 
        'create a point near where we want the tangent line to end, notice sign change from first tangent line
        point2 = New Point3d(arc2.CenterPoint.X - arc2.Radius, arc2.CenterPoint.Y - arc2.Radius, 0.0)
        associativeLineBuilder1.EndTangent.SetValue(arc2, nullView, point2)
 
        Dim nXObject2 As NXObject
        nXObject2 = associativeLineBuilder1.Commit()
 
        Dim objects2() As NXObject
        objects2 = associativeLineBuilder1.GetCommittedObjects()
 
        associativeLineBuilder1.Destroy()
 
 
    End Sub
 
 
    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

Comments

Do I must use Unit for every value. If my part is millimitric why I can't
Use The part Unit.
And please if this is the way NX work, What is the code for reassign all part unit values to millimitric or inches. In other words code to convert part unit.

Thanks In advance

In the first code sample the line will be drawn in the part's unit system. The line will start at (1,2,0) and end at (4,5,0). If the part is metric, the resulting line will be ~4.24 mm long (for an inch part the line will be ~4.24 in long).

The lines were created in part by creating expressions with units. To create these expressions requires the use of a "unit" object. You cannot use the workPart's PartUnit property because it returns an integer indicating whether the part units are inch or metric, it does not return a unit object.

If you need information on how to convert a part's unit system, there is a utility shipped with NX to do the job. In the NX help files, search for the term "ug_convert_part". After some digging in the help files, you may also want to look here: http://www.eng-tips.com/viewthread.cfm?qid=296279

Hi,

I try eventually to create a rectangle in a Sketch plane. I managed easily to draw an ellipse on this specific plane.
Now I'm trying the rectangle. All I see with the journal, is that a rectangle is 4 lines with constraints.

I have a special Sketch plane and an CSYS attached to it. I want to draw in this plane.
But when I record the journal, the coordinates used are the one from the original CSYS, not the last one in use.

Before calling the function to draw the rectangle I've put
pDatumCsys->SetWcsAtCsys();

But it doesn't seem to do anything about coordinates ...
If I want to draw a rectangle centered in in the new CSYS origin, the four points cooridnates will be :
Point1 ( -L/2, l/2,0)
Point2 (+L/2, l/2, 0)
Point3( +L/2,-l/2,0)
Point4( -L/2, -l/2,0)

L : length
l : width
But when I try to create lines with that I end up with an "object not in the plane" ...

My question is : How do I draw lines in a sketch with a specific DatumCSYS ?

The goal of drawing in a Sketch is to be in 2D, but I don't see how to do it ...

Please, help me !