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:
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.
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.
The next two lines removes the parameters of the new point and makes it visible.
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.
[vbnet]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
[/vbnet]
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:
[vbnet]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
[/vbnet]
The following code demonstrates spherical offset and offsets along a vector.
[vbnet]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
[/vbnet]
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.
[vbnet] 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)
[/vbnet]
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.
[vbnet]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
[/vbnet]
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!
Comments
UpdateOption?
What does updateOption really mean?
I tried and set the UpdateOption to "AfterModelling". The program still works. I don't see any difference. Enlighten me, please. :)
re: update option
I'm a bit fuzzy myself on the exact differences among the update options. Here is my current understanding:
"WithinModeling" will update the object during a model update
"AfterModeling" will update the object after the model update
Based on the fact that we have a choice as to when an object updates, I'm assuming that NX keeps track of at least 2 different types of objects: those that directly affect the model and those that are affected by the model. When something changes, NX updates all of the model entities first (sketches, extrudes, blends, etc), then those that are affected by the model (such as drafting dimensions, section cuts, etc). If you change the update option in the example journal, it will probably have no real affect. The journal only creates a few point objects and no other geometry depends on them. If this were a large model where other geometry depended on the point objects (the position of the point drove the position of sketch entities, which created an extrude, which was then subtracted from the model, whose faces were then tapered, and edges blended, etc etc), then using the "AfterModeling" option may affect the update order and final result.
So, if we change the update option in the "large, complicated" model example above, I think one of two things would happen (again, this is purely speculation on my part, I don't know exactly how NX handles these internal updates).
1) The points, and any geometry dependent on them, will update later than the rest of the model, leading to update errors.
2) NX will perform the "after model update" and notice that model objects have changed which will fire off another model update. The end result may be correct, but it may take more time than needed (due to multiple model updates) to get there.