Get correct angle value between two adjacent faces

Get correct angle value between two adjacent faces

Maxim-CADman77
Advisor Advisor
1,804 Views
14 Replies
Message 1 of 15

Get correct angle value between two adjacent faces

Maxim-CADman77
Advisor
Advisor

I'd like to know what I'm missing in my code while trying to get angle value between two adjacent faces

 

 

 

 

Dim body = ThisDoc.Document.ComponentDefinition.SurfaceBodies(1)
Dim edge = body.Edges(1)
Dim face1 = edge.Faces(1)
Dim face2 = edge.Faces(2)
MsgBox(ThisApplication.MeasureTools.GetAngle(face1, face2)*180/PI & " deg",, "Angle")

 

 

 

 

The Dodecahedron (see model attached) has all angles equal to ~"117 deg" but the code returns "63 deg" (angle adjacent to the measured)

MaximCADman77_0-1724018027513.png

Different manual and API output on same input.

 

I know that In this particular case I can get the desired by subtracting the value from 180 deg, but this is not applicable for concave angles

Isn't it an API defect?

 

Dear @MjDeck, could you, please, clarify this?

Please vote for Inventor-Idea Text Search within Option Names

0 Likes
Accepted solutions (1)
1,805 Views
14 Replies
Replies (14)
Message 2 of 15

WCrihfield
Mentor
Mentor

Hi @Maxim-CADman77.  I can't explain the behavior of the manual measure tool, but when doing it by code, I can understand it better.  It goes by the 'Normal' (direction) of the face, and by the order they are selected.  Picture an arrow pointing perpendicularly outward from the center of each face, then when you select a face to measure its angle, it is really selecting that arrow, then when you select the second face, it measures between the angle from the direction of the first face's arrow, to the direction of the second face's arrow.  So the following properties were likely used behind the scenes to figure it out.

Face.Evaluator (as SurfaceEvaluator)

SurfaceEvaluator.GetNormal 

UnitVector 

UnitVector.AngleTo()

 

Edit:  Here is alternate code example, just for additional reference.

Dim body As SurfaceBody = ThisDoc.Document.ComponentDefinition.SurfaceBodies(1)
Dim edge As Edge = body.Edges(1)
Dim face1 As Face = edge.Faces(1)
Dim face2 As Face = edge.Faces(2)
Dim oF1points() As Double = {}
Dim oF1normals() As Double = {}
face1.PointOnFace.GetPointData(oF1points)
face1.Evaluator.GetNormalAtPoint(oF1points, oF1normals)
Dim oF2points() As Double = {}
Dim oF2normals() As Double = {}
face2.PointOnFace.GetPointData(oF2points)
face2.Evaluator.GetNormalAtPoint(oF2points, oF2normals)
Dim oF1Normal, oF2Normal As UnitVector
oF1Normal = ThisApplication.TransientGeometry.CreateUnitVector(oF1normals(0), oF1normals(1), oF1normals(2))
oF2Normal = ThisApplication.TransientGeometry.CreateUnitVector(oF2normals(0), oF2normals(1), oF2normals(2))
Dim dAngle As Double = (oF1Normal.AngleTo(oF2Normal) * (180 / Math.PI))
MsgBox(dAngle.ToString & " deg", vbInformation, "Angle")

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 3 of 15

MjDeck
Autodesk
Autodesk

Hi @Maxim-CADman77 - as Wesley says, the angle measurement uses the surface normal. We should mention that in the documentation for MeasureTools.GetAngle .

 

The angle isn't affected by the order of arguments you pass to the function. The result will be the same when you provide them in either order (face1, face2) or (face2, face1).
The return value should be the smallest angle between the normals. It will always be in the range 0 to 180 degrees. Here's a short test rule:

 

 

Dim face1 = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFacePlanarFilter, "Select Face 1")
Dim face2 = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFacePlanarFilter, "Select Face 2")

If face1 IsNot Nothing And face2 IsNot Nothing Then
	Logger.Info("angle = {0} deg", ThisApplication.MeasureTools.GetAngle(face1, face2)*180/PI )
End If

 

 


Edit: the function is not working the way I would expect it to in all cases. I'll try to find out more.

About convex and concave:
The face normals always point outside the body. (Here's some documentation. Faces on the boundary of a solid body are single-sided faces.)
If two planar faces are adjacent (share an edge), I know of one way to find out if they are convex or concave on the body. But before I post it, I'll try to find out if there's a simpler way.


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 4 of 15

Curtis_Waguespack
Consultant
Consultant

@Maxim-CADman77 , here is what I had on hand. It is a version of what @Cadkunde.nl posted a while back.

 

It's been a bit since I looked at this, but I think I reworked it with the goal of returning the same result as if we used the Measure tool manually.  I don't know how reliable the results are, but it seemed to work well for me when I was working with this in the past. MjDeck might have more straightforward approach though.

 

the original is here:

https://forums.autodesk.com/t5/inventor-programming-ilogic/measure-angle-between-faces-if-angle-is-m...

 

Sub main
	Dim oFace1 = ThisApplication.CommandManager.Pick _
		(SelectionFilterEnum.kPartFacePlanarFilter, "Select first face")
	Dim oFace2 = ThisApplication.CommandManager.Pick _
		(SelectionFilterEnum.kPartFacePlanarFilter, "Select second face")
	Dim oAngle As Double = ThisApplication.MeasureTools.GetAngle(oFace1, oFace2) * 180 / Math.PI

	Dim oComparisonPts1 As Object = GetComparisionPoints(oFace1)
	Dim oComparisonPts2 As Object = GetComparisionPoints(oFace2)

	Dim oDistance1 As Double = ThisApplication.MeasureTools.GetMinimumDistance _
		(oComparisonPts1(0), oComparisonPts2(0))
	Dim oDistance2 As Double = ThisApplication.MeasureTools.GetMinimumDistance _
		(oComparisonPts1(1), oComparisonPts2(1))

	Dim oLine As LineSegment = ThisApplication.TransientGeometry.CreateLineSegment _
		(oComparisonPts1(0), oComparisonPts1(2))
	Dim oIntersectpoint As ObjectsEnumerator = oLine.IntersectWithSurface(oFace2.Geometry)

	If oDistance1 > oDistance2 And Not oIntersectpoint Is Nothing Then
		oQ = "Quadrant 1"
		oAngle = oAngle
	ElseIf oDistance1 > oDistance2 And oIntersectpoint Is Nothing Then
		oQ = "Quadrant 2"
		oAngle = 180 - oAngle
	ElseIf oDistance1 < oDistance2 And Not oIntersectpoint Is Nothing Then
		oQ = "Quadrant 3"
		oAngle = 180 - oAngle
	ElseIf oDistance1 < oDistance2 And oIntersectpoint Is Nothing Then
		oQ = "Quadrant 4"
		oAngle = oAngle
	End If

	MsgBox(oAngle, , oQ)
End Sub

Function GetComparisionPoints(oFace As Face)

	Dim FacePoint1 As Point = oFace.PointOnFace

	Dim Params(1) As Double
	Params(0) = 0
	Params(1) = 0

	Dim Normals(2) As Double
	oFace.Evaluator.GetNormal(Params, Normals)

	Dim FacePoint2 As Point = ThisApplication.TransientGeometry.CreatePoint _
	(FacePoint1.X + Normals(0) * 0.001,
	FacePoint1.Y + Normals(1) * 0.001,
	FacePoint1.Z + Normals(2) * 0.001)

	Dim FacePoint3 As Point = ThisApplication.TransientGeometry.CreatePoint _
	(FacePoint1.X + Normals(0) * 9999,
	FacePoint1.Y + Normals(1) * 9999,
	FacePoint1.Z + Normals(2) * 9999)

	GetComparisionPoints = New Point() {FacePoint1, FacePoint2, FacePoint3 }

End Function

 

EESignature

Message 5 of 15

WCrihfield
Mentor
Mentor

I am not sure if this is what @MjDeck had in mind about dealing with concave vs convex, but I created a custom Function, paired with a custom Enum, for determining if two faces are concave or convex.  Then I used the result of that to determine if the angle between the two faces should be 'as computed' (inside angle), or inverted (outside angle).  It's likely not as error proof as it could be, but it seemed to work OK in my initial testing.

 

It first essentially determines if the two 'input' faces have a common edge.  If they do not, then these faces can not really be labeled either concave or convex, as far as I understand anyways.  Then, it uses the tools already available to us for finding concave or convex edges of a body.  Based on if the 'common edge' is considered concave or convex, that determines if the faces are concave or convex to each other.  Then, as stated before, if they are convex, I invert the measured angle between them to essentially be 360 degrees minus the measured angle, to get its inverse.  You can use that association however you want though.

Sub Main
	Dim oFace1 As Face = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFacePlanarFilter, "Select Face 1")
	If oFace1 Is Nothing Then Return
	Dim oFace2 As Face = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFacePlanarFilter, "Select Face 2")
	If oFace2 Is Nothing Then Return
	Dim eResult As ConcaveConvexOrNeitherEnum = CheckFacesForConcaveOrConvex(oFace1, oFace2)
	Dim dAngle As Double = (ThisApplication.MeasureTools.GetAngle(oFace1, oFace2) * (180/Math.PI))
	If eResult = ConcaveConvexOrNeitherEnum.Concave Then 'inside angle
		'do nothing
	ElseIf eResult = ConcaveConvexOrNeitherEnum.Convex Then 'outside angle
		dAngle = (360 - dAngle) 'invert angle
	End If
	MsgBox("Faces are " & eResult.ToString & " and," & vbCrLf & _
	"Angle = " & dAngle.ToString & " deg.", vbInformation, "iLogic")
End Sub

Public Enum ConcaveConvexOrNeitherEnum
	Concave
	Convex
	Neither
End Enum

Function CheckFacesForConcaveOrConvex(oFace1 As Face, oFace2 As Face) As ConcaveConvexOrNeitherEnum
	'find the common edge between those two faces
	Dim commonEdge As Edge
	For Each oEdge As Edge In oFace1.Edges
		If oEdge.Faces.Count > 1 Then
			For Each oOtherFace In oEdge.Faces
				If oOtherFace Is oFace2 Then
					commonEdge = oEdge
					Exit For
				End If
			Next oOtherFace
		End If
		If commonEdge IsNot Nothing Then Exit For
	Next oEdge
	If commonEdge Is Nothing Then
		'Logger.Debug("No common edge found!", vbExclamation, "iLogic")
		Return ConcaveConvexOrNeitherEnum.Neither
	End If
	Dim oCCEs As EdgeCollection = oFace1.Parent.ConcaveEdges
	'Logger.Info("ConcaveEdges = " & oCCEs.Count)
	Dim oCVEs As EdgeCollection = oFace1.Parent.ConvexEdges
	'Logger.Info("ConvexEdges = " & oCVEs.Count)
	If oCCEs IsNot Nothing AndAlso oCCEs.Count > 0 Then
		For Each oConcaveEdge As Edge In oCCEs
			If oConcaveEdge Is commonEdge Then
				Return ConcaveConvexOrNeitherEnum.Concave
			End If
		Next oConcaveEdge
	End If
	If oCVEs IsNot Nothing AndAlso oCVEs.Count > 0 Then
		For Each oConvexEdge As Edge In oCVEs
			If oConvexEdge Is commonEdge Then
				Return ConcaveConvexOrNeitherEnum.Convex
			End If
		Next oConvexEdge
	End If
	Return ConcaveConvexOrNeitherEnum.Neither
End Function

If this solved your problem, or answered your question, please click ACCEPT SOLUTION .
Or, if this helped you, please click (LIKE or KUDOS) 👍.

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 6 of 15

Curtis_Waguespack
Consultant
Consultant

@WCrihfield attached is my test file where I was measuring the color coded matched face pairs. Your example returns 45 degrees for all pairs.

 

If you have a few minutes can you take a look at this file?

 

this is the result of selecting the blue faces:

Curtis_Waguespack_2-1724094979454.png

 

EESignature

Message 7 of 15

MjDeck
Autodesk
Autodesk
Accepted solution

Here's a version that doesn't use the MeasureTools.GetAngle function. Instead, it uses vector cross product calculations to get directions of lines on the faces. So far, this has given me the same results as the Measure command. Please try it out.
@WCrihfield , I added code similar to what you have to check if the edge is convex. But the way I'm doing it, this isn't needed for the angle calculation. This code could be improved to be more like what you have, and explicitly show convex and concave. (I initially had another idea to check for convex/concave, but I'm glad to find out that it's available directly from the API!)


Mike Deck
Software Developer
Autodesk, Inc.

Message 8 of 15

Cadkunde.nl
Collaborator
Collaborator

Interesting topic. Just replying to find it again after vacation 

Had to do a lot of work with this and bothers me I havent found simple solution.

 

Convex and concave seems a promising method. I can understand how to use it now. Thanks

Message 9 of 15

_dscholtes_
Advocate
Advocate

@MjDeck wrote:

If two planar faces are adjacent (share an edge), I know of one way to find out if they are convex or concave on the body. But before I post it, I'll try to find out if there's a simpler way.


 

How about this:

 


@gerrardhickson wrote:

Well, this feels silly - I just discovered the "SurfaceBodies.ConcaveEdges" edge collection. In my case, this is exactly what I want.


Found in this thread: Re: How to determine if an edge belongs to faces that are inward facing or outward facing?

Message 10 of 15

MjDeck
Autodesk
Autodesk

Hi @_dscholtes_ - yes, it's good that those functions (or properties) are available. I didn't know about them when I made my initial reply, but I found them later. I included ConvexEdges in my reply in message 7 above. @WCrihfield posted a more complete example with tests for both convex and concave edges in message 5.


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 11 of 15

WCrihfield
Mentor
Mentor

Well this has been a very interesting and fun topic.

 

@Curtis_Waguespack Thanks for the sample part.  I never considered that a part could have a single Edge connecting what would normally have been 2 different bodies, and could have 4 different Faces associated with it.  Due to how odd that one central edge is, it is neither concave, nor convex.  You were right about my process (using convex vs concave to determine if I should simply subtract the measured angle from 360 degrees) not being error proof yet.

 

@MjDeck Thanks.  I don't know why I didn't just go the CrossProduct route right away, since I am already pretty familiar with that process, due to the ' view / camera' related topics I was recently involved with.  That was definitely the shortest code path, and seems to require the least processing, to get to the result we were looking for.  Each 'cross product' leaves us with a 'direction' that is perpendicular to both of the 'input' directions, which is super handy sometimes.  Figuring out the correct order to process them in, to control the resulting direction's polarity is the tricky part to picture in my mind.

 

Since this appears to be a 'busier' overall process than many of us had in mind, I decided that a 'helper' Class may be in order, to 'bundle' all that helper code into one overall block of code that can be referenced and called in to help,  which should keep the 'Main' code routine simpler & easier to look at.  I created two different versions of the helper Class.  One version uses the simpler CrossProduct first step shown in Mike's example to get the 'edge direction', and has fewer public properties in the Class.  The second version uses more 'traditional' routes to get the 'edge direction', and offers a few more public properties in the Class.  Both versions use the same final two CrossProduct steps, to get the 2 virtual directions pointing away from the virtual edge, for measuring the angle between.  This longer example also includes a couple new relatively simple, custom Functions (not part of the helper Class) that can be used to determine if an edge is concave or convex.

I will show those 2 here directly, for quicker reference:

 

Private Function EdgeIsConcave(oEdgeToCheck As Inventor.Edge) As Boolean
	For Each oEdge As Inventor.Edge In oEdgeToCheck.Parent.ConcaveEdges
		If oEdge Is oEdgeToCheck Then Return True
	Next
	Return False
End Function

 

and

 

Private Function EdgeIsConvex(oEdgeToCheck As Inventor.Edge) As Boolean
	For Each oEdge As Inventor.Edge In oEdgeToCheck.Parent.ConvexEdges
		If oEdge Is oEdgeToCheck Then Return True
	Next
	Return False
End Function

 

Those 2 complete examples using the 2 different versions of the helper Class are attached as text files, since they are rather long.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 12 of 15

MjDeck
Autodesk
Autodesk

Hi Wesley - those rules look good, but they're not getting the same result as the Measure command in the attached part. They report some angles as 45 degrees instead of 135.


Mike Deck
Software Developer
Autodesk, Inc.

Message 13 of 15

WCrihfield
Mentor
Mentor

@MjDeck You are right.

The difference comes from the first half of my 'GetFaceNormal' Function, where I first attempt to get the 'Normal' from the Plane.Normal property, where Face.Geometry is the Plane object.  Apparently that Normal can point in the opposite direction from the 'Normal' we get when using the SurvaceEvaluator.GetNormalAtPoint method.  Not sure why.  But I did do some follow-up testing on the part, due to a suspicion, and found what I was expecting.  For some of the faces of that part, its Face.IsParamReversed property returned a value of True, meaning the face's Normal is pointing inwards (towards interior of material), instead of outwards (away from material).  I guess maybe that can be used as a lesson for how to avoid that unpredictable situation.  Always use the 'Evaluator' tools, instead of the properties of the underlying 'transient geometry' objects (when possible).  When I commented out that whole If...Else...End If statement, and the code within the 'If' half, it works the same as the manual measure tool, as planned.

So, this:

 

Private Function GetFaceNormal(oFace As Inventor.Face) As Inventor.UnitVector
	If TypeOf oFace.Geometry Is Inventor.Plane Then
		Dim oPlane As Inventor.Plane = oFace.Geometry
		Return oPlane.Normal
	Else
		Dim oPt() As Double = {} : Dim oNorm() As Double = {}
		oFace.PointOnFace.GetPointData(oPt)
		oFace.Evaluator.GetNormalAtPoint(oPt, oNorm)
		Return _InvApp.TransientGeometry.CreateUnitVector(oNorm(0), oNorm(1), oNorm(2))
	End If
End Function

 

...had to be simplified back to this:

 

Private Function GetFaceNormal(oFace As Inventor.Face) As Inventor.UnitVector
	Dim oPt() As Double = {} : Dim oNorm() As Double = {}
	oFace.PointOnFace.GetPointData(oPt)
	oFace.Evaluator.GetNormalAtPoint(oPt, oNorm)
	Return _InvApp.TransientGeometry.CreateUnitVector(oNorm(0), oNorm(1), oNorm(2))
End Function

 

I knew that it was possible for the Normal's of faces to be pointing inwards, but still do not understand when or how it happens to be able to predict it.  It seems to me like I usually saw that in bodies / parts that have been created through Mirror type process.

 

Edit:  Here is another version, where it checks that Face.IsParamReversed, and if True, it reverses the polarity of the Normal before returning it.  I only included the Plane.Normal part in the first place, because I thought that it might require less processing or run faster, when compared with the evaluator tools, but have not done specific testing to see if that is true.

Private Function GetFaceNormal(oFace As Inventor.Face) As Inventor.UnitVector
	If TypeOf oFace.Geometry Is Inventor.Plane Then
		Dim oPlane As Inventor.Plane = oFace.Geometry
		If Not oFace.IsParamReversed Then
			Return oPlane.Normal
		Else
			Dim oDir As UnitVector = oPlane.Normal
			oDir.PutUnitVectorData({-oDir.X, -oDir.Y, -oDir.Z}) 'reverse polarity
			Return oDir
		End If
	Else
		Dim oPt() As Double = {} : Dim oNorm() As Double = {}
		oFace.PointOnFace.GetPointData(oPt)
		oFace.Evaluator.GetNormalAtPoint(oPt, oNorm)
		Return _InvApp.TransientGeometry.CreateUnitVector(oNorm(0), oNorm(1), oNorm(2))
	End If
End Function

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 14 of 15

MjDeck
Autodesk
Autodesk

Hi Wesley - the normal of the Face will always point outside of the body. But the underlying surface normal could point either way. This gives the modeler more flexibility in constructing a solid. It can stitch together different surfaces without having to modify their geometry. I'll try to find a simple case to demonstrate.


Mike Deck
Software Developer
Autodesk, Inc.

Message 15 of 15

MjDeck
Autodesk
Autodesk

About face normals:

Consider a rectangular box. Say the surface normals have the same direction as the face normals. They're all pointing outwards from the box.

Now you use that box to do a Boolean and cut a pocket in another shape. Any face normal that was pointing outward should now be changed to point inward (inward for the original box is now outward for the result solid). The way it's implemented, I'm pretty sure that the solid modeling software only has to set a boolean value on the face to say that the normal is flipped. It doesn't have to modify the surface.


Mike Deck
Software Developer
Autodesk, Inc.