I am trying to programmatically return the surface/curve that contains a point a given X,Y,Z coordinate. The only options I have found are the collection of feSelectOutput functions, but these all use a dialog box. What I am looking for is a way to get the surface/curve that contains the specified coordinate without any user interaction. The same process is achieved through the GUI when a surface/curve is selected so I know it is possible, but I do not see it in the API.
If anyone has details on how to achieve this it would be greatly appreciated.
Solved! Go to Solution.
you can check all Points in your model, which Points have the required coordinates (with a tolerance). When you found these points, you can use the "Curves" point object method on they (see 18.104.22.168 in Help in API section), or CurvesAsSet (22.214.171.124), SurfacesAsSet (126.96.36.199), Solid (188.8.131.52) methods.
You can avoid checking all points in your model using the set object and the "AddCoordinate2" method: it can find points located near a location, all you need to do is pilot the tolerance.
For the rest follow what Peter wrote.
Woudl you by chance be able to provide a code block of how to go about this? I am having issues accomplishing it.
My example is a 1x1x1 box. My coordinate is at <0, 0.5, 0.5>, so right in the middle. For the tolerance I apply <0, 0.25, 0.25>, so it should be able to pick up some point on that surface. However, after calling set.AddCoordinate2(Femap.zDataType.FT_SURFACE, 0, true, coord, tol), set.Count() still returns 0. Event if I create a Point object at <0, 0.5, 0>, which is along one of the edges, and call point.curves(0, out numCurves, out curveIds), the function returns 0 and null.
I'm also curious about the surfacesAsSet. It seems Curves() returns an array, but surfacesAsSet adds the matching surfaces to a set and returns nothing. Does this mean I need to create a temporary set, populate it, query it, and then delete the set each time I want to find a surface?
Something like this:
Sub Main Dim App As femap.model Set App = feFemap() 'user specified location and tolerance Dim coord(2) As Double, tol(2) As Double coord(0) = 0 : coord(1) = 0.5 : coord(2) = 0.5 tol(0) = 0 : tol(1) = 0.25 : tol(2) = 0.25 'parameters for AddCoordinate2 method Dim flags(2) As Boolean, mode(2) As Long For i = 0 To 2 flags(i) = True 'use x y and z mode(i) = 4 'use "at location + tolerance" mode Next Dim p As femap.Point Set p = App.fePoint Dim set1 As femap.Set, set2 As femap.Set Set set1 = App.feSet Set set2 = App.feSet 'find all points at specified location, within specified tolerance set1.AddCoordinate2(FT_POINT,0,flags,mode,coord,tol) 'for each of these points, find all attached surfaces 'inject them into set2 While p.NextInSet(set1.ID) p.SurfacesAsSet(FCC_BOTH,set2.ID,False) Wend 'point(s) is (are) connected to these surfaces: set2.Debug End Sub
'find all points at specified location, within specified tolerance
When I use your code, it works, but both sets have zero entries. I'm wondering if when you state the above, are these points that are internal to femap? Or do these have to be actual points that the user has created in the model?
I increased the tolerance all the way to <0, 0.5, 0.5>, so this encompasses the entire face of the box since the coordinate is in the middle, and AddCoordinate2 still does not place any points in the set.
the API work, but not with your coordinates. There are any points of solid about coordinate <0, 0.5, 0.5>. See in this picture (inner circle is the tolerance <0, 0,25, 0,25> and the outer circle is the tolerance <0, 0.5, 0,5>:
When you use coordinate <0, 0.1, 1>, you get in set2 the surfaces IDs 7, 8 and 9 with tolerance <0, 0.25, 0.25>, because the API find here the point ID10 in solid:
Ah right sorry, I didn't take the time to fully understand what you are asking.
If I understand correctly you want to find which surface "passes by" a given point, eventhough it's not a point belonging to the surface (in the FEMAP sense). That is much MUCH harder. But of course not impossible, this is FEMAP we're talking about
The hard part is to find a concept which will be both quick on execution and robust in all cases. I have an idea for the later, but it will be slow:
loop on surfaces/curves
for each surface/curve, project your location onto it
if the distance between location and projection is smaller than tolerance => bingo!
The obvious problem is that you go through all curves/surfaces...
So something like:
Sub Main Dim App As femap.model Set App = feFemap() 'tolerance Const tol = 1e-4 Dim coord(2) As Double, proj As Variant Dim d As Double Dim surfSet As femap.Set Set surfSet = App.feSet surfSet.AddAll(FT_SURFACE) coord(0) = 0 : coord(1) = 0.5 : coord(2) = 0.5 While surfSet.Next App.feCoordOntoSurface(surfSet.CurrentID,coord,proj) App.feMeasureDistance(coord,proj,d) If d <= tol Then App.feAppMessage(FCM_NORMAL,"Point is on surface #" & CStr(surfSet.CurrentID)) Exit While 'if you suppose point is only on ONE surface then you might as well exit End If Wend End Sub
But again, if you have many surfaces/curves, this is tedious!
Unfortunately finding a fast concept is hard (or at least seems hard to me). First off I think you are mistaken when you say "The same process is achieved through the GUI when a surface/curve is selected so I know it is possible": graphical selection is probably very different programmatically than geometrical selection, I have doubts regarding whether one can do what the other does.
I think the "true" solution would go through surface facets, perhaps building a clever tree from them (centroid and vertexes) and finding the "close" ones, and only then looking for plausible answers. So not an easy code...
Oh and one last thing: surfaces have a "InsideXYZ" method, but this will check whether the projection of the location is inside the surface (or at least this is true for planar surfaces), so it will not work for what you are trying to do.
As long as you are using v11.1 or later there is another alternative. You can use the application method feMeasureDistanceBetweenGeometry( ). This does require you to create a point instead of just using coordinates, but that isn't hard... simply create a Point object, specify the coordinates you want and Put( ) the object. You can then create a Set object of all of your surfaces.. or at least all that you are interested in testing.. and use feMeasureDistanceBetweenGeometry() to find the minimum distance between the point and the surfaces. FEMAP will test all of the surfaces and return the one that is the closest, along with the location of the closest approach and the distance between the two. To cleanup, you can also simply delete the point.. assuming you don't want it to persist in your model.
Yes, essentially I will have a datum in a CAD model whose <X,Y,Z> coordinate will be directly on a surface, curve, etc. So ideally I would have a coordinate in the center of the surface, and from the coordinate be able to figure out the corresponding femap surface/curve ID. So yes, the point is not a Point construct in the femap model.
My thought behind the GUI accomplishing this is that when you move the mouse and hover over surfaces, the corresponding surface is highlighted. However, the more I try it I do realize it is only an estimate because of the 2D screen coordinates (hovering over one surface can highlight another -- not what you want for a programmatic solution!).
It seems as if the temporary solution is to do as you say and loop through the points. I will know whether it is a surface or curve, so that will help some.