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
A 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
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:
- Try to record a journal.
- Use the search (or index) function to look up the object (class) you are interested in.
- Use the search (or index) function to look up the builder for the object you are interested in.
- Step through the process in interactive NX to look for new, relevant search terms.
- Use the search function and look at the top returned items.
- 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:
- 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).
- 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.
- The DimensionBuilder has its own properties and methods, but again, nothing that would indicate if the text was manual.
- 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).
- 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.
- 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 0 | column 1 | |
row 0 | word 1 | definition 1 |
row 1 | word 2 | definition 2 |
... | ... | ... |
row n | word n | definition 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):
DontUpdate | No update occurs |
WithinModeling | Updates within modeling (e.g. a feature) |
AfterModeling | Updates after modeling (e.g. a dimension) |
AfterParentBody | Updates 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
Public Function GetUnloadOption
What is the " Public Function GetUnloadOption " for?
Carlo Tony Daristotile
Re: GetUnloadOption
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
Another Fractal Tree
'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
Re: Another Fractal Tree
Very cool design.
Thanks for sharing.
MAPLE LEAF
'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
HOW TO ADD "ARC OR LINE OR POINT" TO THE MODEL REFERENCE SET ?
Carlo Tony Daristotile
Re: Adding Objects To The Reference Set
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.
Re: Adding Objects To The Reference Set
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
Re: Model Reference Set
To find a part's "model" reference set, have a look here:
http://www.nxjournaling.com/?q=content/model-reference-set
Delete PtObj1 And PtObj2 After The Line Creation
I am not able to delete ptObj1 and ptObj2 after the line is created. I tried the following:
and
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.
Re: Delete 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.
Hello,
hello,
Where can I find commands and their examples:
- workPart.Curves.CreateLine
- workPart.Points.CreatePoint
- workPart.Scalars.CreateScalar
etc..
Kien
Re: Commands And Examples
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 center, the 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.