Checking for duplicate Point (and/or Meshpoint) in NX.CAE?

To all

I do not believe NX.CAE has an option to warn user if points (or MeshPoints) already exist at a given position in one tries creating a new one (at the same position). I am therefore thinking about a “small” program which could loop through all the points selected (and/or Meshpoints), warn user of duplicate exist and delete if requested (?)

Possible scope is
Select a list of points (or Meshpoints)
Check if duplicate points exist at a given position
Delete duplicate points (to keep only 1)

Does anyone have any good idea/suggestions, etc on the best way to start?

Thanks
Regards
JXB

I'd suggest sorting the list of points by one of the coordinates, let's say the X coordinate, for example; then you can loop through the list looking for duplicates. If the X coordinate matches, continue checking on the Y and Z coordinates. If it is a match, add it to a list of duplicates (these will be deleted later). If it isn't a match, move along to the next point in the list.

The code below illustrates this strategy. The journal will create point objects, run the code in a new file (or use the undo function to get rid of them later). The subroutine "RemoveDuplicatePoints" is the area to focus on, the rest is mostly setup and output.

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module1

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()

Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

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

Const undoMarkName As String = "remove duplicates"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

Dim pointList As New List(Of Point)
InitializePoints(pointList, 10, 3)

lw.WriteLine("original list of points")
For Each pt As Point In pointList
lw.WriteLine(pt.Coordinates.ToString)
Next
lw.WriteLine("")

RemoveDuplicatePoints(pointList)
lw.WriteLine("duplicates removed, count: " & pointList.Count.ToString)

lw.WriteLine("list of points with duplicates removed")
For Each pt As Point In pointList
lw.WriteLine(pt.Coordinates.ToString)
Next

lw.Close()

End Sub

Sub InitializePoints(ByRef ptList As List(Of Point), ByVal numPts As Integer, ByVal numDuplicates As Integer)

Dim rnd1 As New Random()
Dim max As Integer

If theSession.Parts.Work.PartUnits = Part.Units.Inches Then
max = 10
Else 'metric file
max = 250
End If

Dim myPt1 As New Point3d(0, 0, 0)

'create random points
For i As Integer = 0 To numPts - 1
myPt1.X = rnd1.Next(-max, max) + rnd1.NextDouble
myPt1.Y = rnd1.Next(-max, max) + rnd1.NextDouble
myPt1.Z = rnd1.Next(-max, max) + rnd1.NextDouble
Dim newPt As Point
newPt = theSession.Parts.Work.Points.CreatePoint(myPt1)
newPt.SetVisibility(SmartObject.VisibilityOption.Visible)
ptList.Add(newPt)
Next

'create duplicate points
'duplicate the first n points at random locations in the list
Dim locations As New List(Of Integer)
For i As Integer = 0 To numDuplicates - 1
lw.WriteLine("i = " & i.ToString)
Dim loc As Integer
Do
loc = rnd1.Next(numDuplicates, ptList.Count - 1)
'lw.WriteLine("loc = " & loc.ToString)
'lw.WriteLine("locations.Contains(loc) = " & locations.Contains(loc).ToString)
Loop Until Not locations.Contains(loc)
locations.Add(loc)

ptList.Item(loc).SetCoordinates(ptList.Item(i).Coordinates)
ptList.Item(loc).RedisplayObject()

Next

End Sub

Function SortPointsX(ByVal x As Point, ByVal y As Point) As Integer

If x.Coordinates.X > y.Coordinates.X Then
Return 1
End If

If x.Coordinates.X < y.Coordinates.X Then
Return -1
End If

If x.Coordinates.X = y.Coordinates.X Then
Return 0
End If

End Function

Sub RemoveDuplicatePoints(ByRef ptList As List(Of Point))

Dim ptDelete As New List(Of Point)

ptList.Sort(AddressOf SortPointsX)

lw.WriteLine("sorted list of points")
For Each temp As Point In ptList
lw.WriteLine(temp.Coordinates.ToString)
Next
lw.WriteLine("")

Dim myModelingTolerance As Double
theUfSession.Modl.AskDistanceTolerance(myModelingTolerance)

For i As Integer = 0 To ptList.Count - 2
For j As Integer = i + 1 To ptList.Count - 1
Dim xDiff As Double = Math.Abs(ptList.Item(i).Coordinates.X - ptList.Item(j).Coordinates.X)
Dim yDiff As Double = Math.Abs(ptList.Item(i).Coordinates.Y - ptList.Item(j).Coordinates.Y)
Dim zdiff As Double = Math.Abs(ptList.Item(i).Coordinates.Z - ptList.Item(j).Coordinates.Z)

lw.WriteLine("point 'i = " & i.ToString & "': " & ptList.Item(i).Coordinates.ToString)
lw.WriteLine("point 'j = " & j.ToString & "': " & ptList.Item(j).Coordinates.ToString)
lw.WriteLine("xDiff: " & xDiff.ToString)
lw.WriteLine("yDiff: " & yDiff.ToString)
lw.WriteLine("zDiff: " & zdiff.ToString)

If xDiff <= myModelingTolerance Then
lw.WriteLine("X coordinates are within tolerance")
'possible match, keep checking
If yDiff <= myModelingTolerance Then
lw.WriteLine("Y coordinates are within tolerance")
'possible match, check on Z coordinate
If zdiff <= myModelingTolerance Then
'it is a duplicate, remove it from the list
lw.WriteLine("Z coordinates are within tolerance, this is a duplicate")
lw.WriteLine("adding point to the delete list, continue checking with next 'j' point")
lw.WriteLine("")
'add the point to the delete list if it is not already there
If Not ptDelete.Contains(ptList.Item(i)) Then
ptDelete.Add(ptList.Item(i))
End If
Else
Continue For
End If
Else
lw.WriteLine("Y coordinates NOT within tolerance, skipping to next 'j' point")
lw.WriteLine("")
'Y coordinate is not a match, skip to next "j" point
Continue For
End If
Else
lw.WriteLine("X coordinates NOT witin tolerance, skipping to next 'i' point")
lw.WriteLine("")
'X coordinate is not a match, skip to next "i" point
'no other point in the list is a possible match because the list is sorted
'the rest of the "j" points will be getting farther away from the "i" point
Exit For
End If
Next
Next

For Each temp As Point In ptDelete
ptList.Remove(temp)
Next

'delete any duplicate points found

Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Delete duplicate points")

theSession.UpdateManager.ClearErrorList()

Dim nErrs1 As Integer
nErrs1 = theSession.UpdateManager.AddToDeleteList(ptDelete.ToArray)

Dim nErrs2 As Integer
nErrs2 = theSession.UpdateManager.DoUpdate(markId1)

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

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

End Function

End Module

You must have had something on the side ready surely!!! I have only managed to code the "select the points" (I found the function in another discussion) and

If SelectPoints("Select points", thePoints) = Selection.Response.Cancel Then
Return
End If

For Each thepoint as Point In thePoints
'get co-ordinate
lw.WriteLine("thePoint.GetType.ToString = " & thePoint.GetType.ToString)
lw.WriteLine("coordinates: " & thePoint.Coordinates.ToString)
lw.WriteLine("")
Next thepoint

after that I had to go back to work!

I have just printed a copy of the kindly provided example and will go through at leisure tonight. A fair bit for my small brain to take

Thanks
Regards

It is very similar to code in the "find duplicate sheet bodies" journal. I had some help from FrankSwinks on that one; he suggested the method for searching through the items.

Also, I made a very small change in the code posted above. In the RemoveDuplicatePoints sub, I changed this:

ptDelete.Add(ptList.Item(i))

to this:

If Not ptDelete.Contains(ptList.Item(i)) Then
ptDelete.Add(ptList.Item(i))
End If

That way the duplicates will only be added to the 'delete' list once. This can be important for 'real world' use.

Thanks for that NXjournaling. Added a couple of things to use the code in the "real" world.

1. Will be looking at adding an option to add the duplicated point to a group. Hoping to do that in a fem part.
2. added a function to select the points.
2.1. No "pretty" as in the Main() I then loop to allocate each member of the array (returned by the select function) to the List (of Point). I need to find a way of returning a List directly
2.2. Also need to look at a way of selecting either Point or Mesh Point (which obviously only works in a FEM part!) Thinking of a question to the user (Do you want Point or MeshPoint? if part =FEM Else just Point) which could be used to set the selectionMask_array(0)

Regards
JXB

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module1

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()
Dim theUISession As UI = UI.GetUI
Dim lw As ListingWindow = theSession.ListingWindow

#Region "Main"
Sub Main()

Dim theSelectedPoints() As NXObject
Dim pointList As New List(Of Point)

If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

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

If SelectPoints("Select points", theSelectedPoints) = Selection.Response.Cancel Then
Return
End If

Dim N as Integer = Ubound(theSelectedPoints)

If N = -1 Then
lw.Writeline("No points selected - Terminating")
Return
Else
lw.Writeline("Number of points selected: " & N.ToString)
lw.Writeline("")
For Each pt as Point In theSelectedPoints
pointList.Add(pt)
REM lw.WriteLine("coordinates: " & pt.Coordinates.ToString)
Next pt
End if

'Ask user if duplicate points should be deleted
Dim answer As Integer
dim bDeleteDuplicatePt As Boolean
'Answer = 1 = Yes Answer = 2 = No Otherwise the returned value will be -2
answer = theUISession.NXMessageBox.Show("Delete Points", _
NXMessageBox.DialogType.Question, _
"Delete duplicate point(s) if found?")

If answer = 1 Then
bDeleteDuplicatePt = True
Else
bDeleteDuplicatePt=False
lw.WriteLine("Duplicate points will not be deleted but added to group 'Duplicate Points' instead")
End if

'Check for duplicate
RemoveDuplicatePoints(pointList,bDeleteDuplicatePt)

End Sub
#End Region

#Region "Select Points"
Function SelectPoints(ByVal prompt As String, byRef selObj() As NXObject) As Selection.Response

Dim theUI As UI = UI.GetUI
Dim title As String = "Select points to check for duplication"
Dim includeFeatures As Boolean = False
Dim keepHighlighted As Boolean = False
Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific
Dim scope As Selection.SelectionScope = Selection.SelectionScope.WorkPart
Dim selectionMask_array(0) As Selection.MaskTriple

With selectionMask_array(0)
.Type = UFConstants.UF_point_type
.Subtype = UFConstants.UF_point_subtype
End With

Dim resp As Selection.Response = theUI.SelectionManager.SelectObjects(prompt, title, scope, selAction, _
includeFeatures, keepHighlighted, _
selectionMask_array, selobj)
If resp = Selection.Response.Ok Then
Return Selection.Response.Ok
Else
Return Selection.Response.Cancel
End If

End Function
#End Region

#Region "RemoveDuplicatePoints()"
Sub RemoveDuplicatePoints(ByRef ptList As List(Of Point),bDeletePt As Boolean)

Dim iduplicatefound As Integer = 0
Dim ptDelete As New List(Of Point)

ptList.Sort(AddressOf SortPointsX)

REM lw.WriteLine("sorted list of points")
REM For Each temp As Point In ptList
REM lw.WriteLine(temp.Coordinates.ToString)
REM Next
REM lw.WriteLine("")

Dim myModelingTolerance As Double
theUfSession.Modl.AskDistanceTolerance(myModelingTolerance)

For i As Integer = 0 To ptList.Count - 2
For j As Integer = i + 1 To ptList.Count - 1
Dim xDiff As Double = Math.Abs(ptList.Item(i).Coordinates.X - ptList.Item(j).Coordinates.X)
Dim yDiff As Double = Math.Abs(ptList.Item(i).Coordinates.Y - ptList.Item(j).Coordinates.Y)
Dim zdiff As Double = Math.Abs(ptList.Item(i).Coordinates.Z - ptList.Item(j).Coordinates.Z)

REM lw.WriteLine("point 'i = " & i.ToString & "': " & ptList.Item(i).Coordinates.ToString)
REM lw.WriteLine("point 'j = " & j.ToString & "': " & ptList.Item(j).Coordinates.ToString)
REM lw.WriteLine("xDiff: " & xDiff.ToString)
REM lw.WriteLine("yDiff: " & yDiff.ToString)
REM lw.WriteLine("zDiff: " & zdiff.ToString)

If xDiff <= myModelingTolerance Then
'lw.WriteLine("X coordinates are within tolerance")
'possible match, keep checking
If yDiff <= myModelingTolerance Then
'lw.WriteLine("Y coordinates are within tolerance")
'possible match, check on Z coordinate
If zdiff <= myModelingTolerance Then
'it is a duplicate, remove it from the list
'lw.writeline("duplicate point found")
iduplicatefound +=1
'lw.WriteLine("Z coordinates are within tolerance, this is a duplicate")
'lw.WriteLine("adding point to the delete list, continue checking with next 'j' point")
'lw.WriteLine("")

If Not ptDelete.Contains(ptList.Item(i)) Then
ptDelete.Add(ptList.Item(i))
End If

Else
Continue For
End If
Else
'lw.WriteLine("Y coordinates NOT within tolerance, skipping to next 'j' point")
'lw.WriteLine("")
'Y coordinate is not a match, skip to next "j" point
Continue For
End If
Else
'lw.WriteLine("X coordinates NOT within tolerance, skipping to next 'i' point")
'lw.WriteLine("")
'X coordinate is not a match, skip to next "i" point
'no other point in the list is a possible match because the list is sorted
'the rest of the "j" points will be getting farther away from the "i" point
Exit For
End If
Next
Next

if iduplicatefound <> 0 Then
lw.writeline("Total duplicate point found: " & iduplicatefound.ToString)
Else
lw.writeline("No duplicate point found")
End if

If bDeletePt = True And iduplicatefound <> 0 Then
For Each temp As Point In ptDelete
ptList.Remove(temp)
Next

'delete any duplicate points found
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "Delete duplicate points")

theSession.UpdateManager.ClearErrorList()

Dim nErrs1 As Integer
nErrs1 = theSession.UpdateManager.AddToDeleteList(ptDelete.ToArray)

Dim nErrs2 As Integer
nErrs2 = theSession.UpdateManager.DoUpdate(markId1)
Else 'Add to group
'to be added
End if

End Sub
#End Region

#Region "sort"
Function SortPointsX(ByVal x As Point, ByVal y As Point) As Integer

If x.Coordinates.X > y.Coordinates.X Then
Return 1
End If

If x.Coordinates.X < y.Coordinates.X Then
Return -1
End If

If x.Coordinates.X = y.Coordinates.X Then
Return 0
End If

End Function
#End Region

#Region "Init"
Sub InitializePoints(ByRef ptList As List(Of Point), ByVal numPts As Integer, ByVal numDuplicates As Integer)

Dim rnd1 As New Random()
Dim max As Integer

If theSession.Parts.Work.PartUnits = Part.Units.Inches Then
max = 10
Else 'metric file
max = 250
End If

Dim myPt1 As New Point3d(0, 0, 0)

'create random points
For i As Integer = 0 To numPts - 1
myPt1.X = rnd1.Next(-max, max) + rnd1.NextDouble
myPt1.Y = rnd1.Next(-max, max) + rnd1.NextDouble
myPt1.Z = rnd1.Next(-max, max) + rnd1.NextDouble
Dim newPt As Point
newPt = theSession.Parts.Work.Points.CreatePoint(myPt1)
newPt.SetVisibility(SmartObject.VisibilityOption.Visible)
ptList.Add(newPt)
Next

'create duplicate points
'duplicate the first n points at random locations in the list
Dim locations As New List(Of Integer)
For i As Integer = 0 To numDuplicates - 1
lw.WriteLine("i = " & i.ToString)
Dim loc As Integer
Do
loc = rnd1.Next(numDuplicates, ptList.Count - 1)
'lw.WriteLine("loc = " & loc.ToString)
'lw.WriteLine("locations.Contains(loc) = " & locations.Contains(loc).ToString)
Loop Until Not locations.Contains(loc)
locations.Add(loc)

ptList.Item(loc).SetCoordinates(ptList.Item(i).Coordinates)
ptList.Item(loc).RedisplayObject()

Next

End Sub
#End Region

#Region "unloadoption"
Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately
End Function
#End Region
End Module

Thanks
Regards

Thinking about it I wonder if it would be better to remove the "Select Points" option and let the programme check all the points in the (displayed) part

Thanks
Regards

> would be better to remove the "Select Points" option and let
> the programme check all the points in the (displayed) part

Good software engineering principles tell us that selection and unduplication are two indepenedent things, so they should be two separate functions. So, there should be:
(1) A function that removes duplicates from a given list of points
(2) A function that asks the user to select points, and returns a list of the selected points.
Then, you can combine #1 and #2 in any way you like, or you can use them independently.

When you write this sort of algorithm, you have to be careful to avoid so-called "n-squared" behaviour. If you have n points, and you compare each one with each other one, than that's (roughly) n*n comparisons. If n is large, you're in big trouble.

I'd recommend that you first sort the points by distance to the origin. Then, any identical ones will end up adjacent to each other in the sorted list, and will be easy to identify. Sorting is an n*log(n) process, which is a lot better than n*n for large n. You can use the standard .NET sorting function. http://www.codeproject.com/Tips/323985/Sorting-using-Csharp-Lists

Thanks ciao for the reminders. A I am not a software engineer so I dispense myself with the good practise of this field. Just kidding. Always happy to learn good/standard practise even if I am just a "Sunday" programmer.

I was vaguely aware that having a very large numbers of Point to process may take some time (didn't know it was "called n-squared" behaviour though). As the programme is tailored to me need I do not think I will ever have more than a couple of hundred but I get your point. I had in mind of doing a few timing tests.

I have taken on board your suggestion about keeping the functions separate and thinking alone the following lines

user selection of all the points to be checked
SelectionPoint()
checkifduplicate(ptselected)
If checkifduplicate(ptselected).count <> 0 And userchoice=True Then deleteduplicate()

Need to go through the detail as the function checkifduplicate() should returns a List() but I would like it to deal with Point and MeshPoint. Can one have in input something like

Sub CheckIfDuplicate(ptListSelected As List(Of NXObject) As List (Of NxObject)

On the use of SelectionManager

theUI.SelectionManager.SelectObjects

I see that on the interface displayed to the user when the function is called that the option 'Select All' is available. Is there a way of calling this function and "forcing" the selection to 'select all'?

on MeshPoint
I have gone through the file uf_object_types.h to find the type and subtype for a meshpoint to be used in the selection.MaskTriple but there was not subtype defined. Does this make sense?

Thanks
Regards
JXB

Thanks
Regards

If there are no subtypes defined for the given type, use the subtype value of 0 (zero) or UF_all_subtype (value -1). Either should work.

If subtypes are defined in later versions of NX, you'll need to update your code accordingly.

thanks. Looks like the filter is working

Dim answer As String = ""
Dim iuserchoice As Integer
Do
answer = NXInputBox.GetInputString("Point Selection", "Point or MeshPoint to be selected? 1=Point, 2=MeshPoint", "1")
'if cancel is pressed, exit sub
If answer = "" Then Exit Sub
Loop Until Integer.TryParse(answer, iuserchoice)

Select Case iuserchoice

Case 1
With selectionMask_array(0)
.Type = UFConstants.UF_point_type
.Subtype = UFConstants.UF_point_subtype
End With
Case 2
With selectionMask_array(0)
.Type = UFConstants.UF_meshpoint_type
.Subtype = UFConstants.UF_all_subtype
End With
End Select

Thanks
Regards