Am I overcomplicating this..?

Am I overcomplicating this..?

AlexFielder
Advisor Advisor
391 Views
0 Replies
Message 1 of 1

Am I overcomplicating this..?

AlexFielder
Advisor
Advisor

Hi all,

 

I am working on a customer project and have a tricky trigonometry problem that I need fresh eyes to take a look at 😲 .

 

Basically, the customer needs to place {widget} around the perimeter of a building (any new-build) starting in the "corners" - which may or may not have an acute/obtuse angle depending on the shape of the building.

 

Thus far, we have seen example building plans from them that are rectangular, L-Shaped, to residential designs that have exterior elements at 45° (depending upon how Inventor decides it wants to measure the angle! 🙄 ) to everything else.

 

It was quickly understood that for this to work, the customer needs to spend some time on the AutoCAD plans such that the perimeter of the building has a polyline sketched around it in an anti-clockwise direction*. This provides the Inventor API with the necessary "Direction" vectors we need to determine the "shape" of the corner in order to work out where to place {widget}.

 

To that end, the attached .ipt file contains the necessary geometry such that the following rule will allow me to calculate (at this time) around 30 different "angles to X axis" in the various quadrants between 0° and 360°, but not all cases are returning accurate results and it's extremely frustrating.

 

AddReference "System.XML"
'AddReference "C:\Users\alex_\OneDrive\Documents\GitHub\Archive\iLogicExternalDebug\packages\NUnit.3.13.2\lib\net45\nunit.framework.dll"
'AddReference "Microsoft.VisualStudio.TestTools.UnitTesting"
'AddVbFile "GraphicsDataSetsExtensions"
AddVbFile "ExtensionsGeneralExtensions"
AddVbFile "ClassMasonrySupport"
AddVbFile "ClassHelperClasses"

Option Explicit On
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Imports Microsoft.VisualStudio.TestTools.UnitTesting
''' Attempts to place a point and then rotate it to the correct location using the maths here: https://matthew-brett.github.io/teaching/rotation_2d.html
Sub main()
	iLogicVb.UpdateWhenDone = True
	'iLogic Unit testing framework:
	If RuleArguments.Exists("TestValues") Then
		Dim RulePassedTestParams = RuleArguments.Value("TestValues")
		Dim TestParams As TestParameters = New TestParameters(ThisApplication, ThisDoc.Document)
		If Not RulePassedTestParams Is Nothing Then
			TestParams = RulePassedTestParams
		End If
	End If
	Dim PartDoc As PartDocument = ThisApplication.ActiveDocument
    Dim PartDef As PartComponentDefinition = PartDoc.ComponentDefinition
	
	Dim InventorExteriorWall As Object = ThisApplication.CommandManager.Pick( _
        SelectionFilterEnum.kSketchCurveLinearFilter, _
        "Select an Exterior Wall to use as the base for the new wall." _
        )
    Dim InventorAdjoiningWall As Object = ThisApplication.CommandManager.Pick( _
        SelectionFilterEnum.kSketchCurveLinearFilter, _
        "Select an Adjoining Wall to use as the base for the new wall." _
        )
    Dim SystemRunDef As MasonrySupport.MasonrySupportRun = New MasonrySupport.MasonrySupportRun
	
	oXAxis = PartDef.WorkAxes.Item(1)
	
    SystemRunDef.ExteriorWall = GetDatumEdgeFromInventorGeom(PartDoc, PartDef, InventorExteriorWall, "exterior AutoCAD (brick/block) line", true, WorkPointPrefix :="ExteriorWall")
	SystemRunDef.AdjoiningWall = GetDatumEdgeFromInventorGeom(PartDoc, PartDef, InventorAdjoiningWall, "adjoining AutoCAD wall line", true, WorkPointPrefix :="AdjoiningWall")

	Dim oPoint As Point = ThisApplication.TransientGeometry.CreatePoint(SystemRunDef.ExteriorWall.StartPoint.X, _
																			SystemRunDef.ExteriorWall.StartPoint.Y, _
																			SystemRunDef.ExteriorWall.StartPoint.Z)

	Dim AngleBetweenChosenWalls As Double = RadiansToDegrees(SystemRunDef.ExteriorWall.Direction.AngleTo(SystemRunDef.AdjoiningWall.Direction))
	Dim BisectorAngle as Double = 180 - (AngleBetweenChosenWalls / 2)
	Dim BisectorAngleAlt As Double = 360 - RadiansToDegrees((SystemRunDef.ExteriorWall.Direction.AngleTo(SystemRunDef.AdjoiningWall.Direction) / 2))
	Dim angleTest as double = 360 - AngleBetweenChosenWalls
	
	Dim DefaultOffset As Double = 3.2
	Dim DefaultAngle As Double = 0
	Dim CalculatedAngle As Double = 0
	Dim DefaultAngleRadians As Double = 0
	Dim ExtWallDirectionX As Double = SystemRunDef.ExteriorWall.Direction.X
	Dim ExtWallDirectionY As Double = SystemRunDef.ExteriorWall.Direction.Y
	Dim AdjWallDirectionX As Double = SystemRunDef.AdjoiningWall.Direction.X
	Dim AdjWallDirectionY As Double = SystemRunDef.AdjoiningWall.Direction.Y
	Dim AngleBetweenWallVectors as Double = RadiansToDegrees(Math.Atan2(AdjWallDirectionY - ExtWallDirectionY, AdjWallDirectionX - ExtWallDirectionX))
	Dim WallAngleToXAxis As Double = SystemRunDef.ExteriorWall.AngleToXAxis
	Dim Hypotenuse As Double = 0
	Dim RotationAngle As Double = 0
' Break
	DefaultAngle = WallAngleToXAxis
	CalculatedAngle = AngleBetweenWallVectors
	Hypotenuse = DefaultOffset / Math.Cos(DegreesToRadians(BisectorAngle))

	'if the axis is at 0, 90, 180 or 270 one or the other of these will be zero, or very close to it.
	If ExtWallDirectionY > 0 and ExtWallDirectionX > 0 then 'WallAngleToXAxis is located between 12:00 and 3:00
Break
		DefaultAngle = 180 - DefaultAngle
		If AngleBetweenWallVectors < 0 then
			RotationAngle = DefaultAngle + (180 + CalculatedAngle)
		Else
			RotationAngle = DefaultAngle + CalculatedAngle
		End If
	Else If ExtWallDirectionY < 0 and ExtWallDirectionX < 0 Then 'WallAngleToXAxis is located between 6:00 and 9:00
Break
		DefaultAngle = 180 - DefaultAngle
		If AngleBetweenWallVectors < 0 then
			RotationAngle = DefaultAngle + (180 + CalculatedAngle)
		Else
			RotationAngle = DefaultAngle + CalculatedAngle
		End If
	Else If ExtWallDirectionY < 0 and ExtWallDirectionX > 0 Then 'WallAngleToXAxis is located between 3:00 and 6:00
Break
		DefaultAngle = 180 + DefaultAngle
		If AngleBetweenWallVectors < 0 then
			RotationAngle = DefaultAngle + (180 + CalculatedAngle)
		Else
			RotationAngle = DefaultAngle + CalculatedAngle
		End If
	Else If ExtWallDirectionY > 0 and ExtWallDirectionX < 0 Then 'WallAngleToXAxis is located between 9:00 and 12:00
Break
		' DefaultAngle = 180 - DefaultAngle
		If AngleBetweenWallVectors < 0 then
			RotationAngle = DefaultAngle + (180 + CalculatedAngle)
		Else
			RotationAngle = DefaultAngle + CalculatedAngle
		End If
	Else

	End If

	DefaultAngleRadians = DegreesToRadians(DefaultAngle)

'works but not thoroughly enough.
	' If AngleBetweenWallVectors < 0 then
	' 	RotationAngle = DefaultAngle + (180 + CalculatedAngle)
	' Else
	' 	RotationAngle = DefaultAngle + CalculatedAngle
	' End If

	If DefaultAngle < 180 then
		RotationAngle = RotationAngle + ((180 - DefaultAngle) * 2)
	Else

	End If

	Dim NewPoint As Point = PolarPoints(oPoint, DefaultAngleRadians, Hypotenuse)
	Dim NewWPoint As WorkPoint = PartDef.WorkPoints.AddFixed(NewPoint)
	' Dim NewAxis As WorkAxis = PartDef.WorkAxes.AddByTwoPoints(PartDef.WorkPoints.Item(1), NewWPoint)
	NewWPoint.Name = NewWPoint.Name.Replace("Work Point", "AngleToXAxis-" & Parameter("AngleToXAxis") & "-correct-not-visible")
	Dim RotatedPoint As Point = GetRotatedPoint(NewPoint, DegreesToRadians(RotationAngle))
	Dim RotatedWPoint As WorkPoint = PartDef.WorkPoints.AddFixed(RotatedPoint)
	RotatedWPoint.Name = RotatedWPoint.Name.Replace("Work Point", "AngleBetweenWalls-" & Parameter("AngleBetweenWalls") & "-correct-not-visible")
	' Dim RotatedAxis As WorkAxis = PartDef.WorkAxes.AddByTwoPoints(NewWPoint, RotatedWPoint)
End Sub

Public Function BuildPointForTest(ByVal AngleToXAxis As Double, ByVal AngleBetweenWalls As Double)
	
End Function


Public oXAxis As WorkAxis = Nothing
Public ReadOnly DefaultTolerance As Double = 0.001
Public ReadOnly kRadiansToDegrees As Double = 180.0 / Math.PI
Public ReadOnly kDegreesToRadians As Double = Math.PI / 180.0

Public Function GetDatumEdgeFromInventorGeom(ByVal PartDoc As PartDocument, _
										ByVal oDef As PartComponentDefinition, _
										ByVal InventorLine As SketchLine, _
										ByVal SelectionPrompt As String, _
										Optional WorkPointsForConstructionPurposesOnly As Boolean = False, _
										Optional WorkPointPrefix As String = "") As MasonrySupport.DatumEdgeFromAutoCAD
	Dim Datum As MasonrySupport.DatumEdgeFromAutoCAD = New MasonrySupport.DatumEdgeFromAutoCAD
	Dim WorkGeomColl As ObjectCollection = ThisApplication.TransientObjects.CreateObjectCollection()
	
	
	Dim startPoint As Point = ThisApplication.TransientGeometry.CreatePoint(InventorLine.StartSketchPoint.Geometry.X, _
																			InventorLine.StartSketchPoint.Geometry.Y, 0)
	Dim NeedToAddStartPointWP As Boolean = True
	Dim endPoint As Point = ThisApplication.TransientGeometry.CreatePoint(InventorLine.EndSketchPoint.Geometry.X, _
																			InventorLine.EndSketchPoint.Geometry.Y, 0)
	Dim NeedToAddEndPointWP As Boolean = True
	Dim oTempWorkAxis As WorkAxis = Nothing
	Dim NeedToAddDirectionAxis As Boolean = True
	Dim oStartPoint As WorkPoint = Nothing
	Dim oEndPoint As WorkPoint = Nothing
	Dim LineDirection As UnitVector = ThisApplication.TransientGeometry.CreateUnitVector(InventorLine.Geometry.Direction.X, _
																							InventorLine.Geometry.Direction.Y, _
																							0)

	If oDef.WorkPoints.Count > 2 Then ' we have work to do. (but isn't the count always > 0 because of the center point..?)
		'check if startpoint's point object is used by an existing WorkPoint and use that if so, otherwise add a new workpoint.
		For Each wp As WorkPoint In oDef.WorkPoints
			If wp.Point.IsEqualTo(startPoint, DefaultTolerance) Then
				NeedToAddStartPointWP = False
				oStartPoint = wp
				oStartPoint.Visible = True 'in case it happens to be the origin point.
				Exit For
			End If
		Next
		For Each wp As WorkPoint In oDef.WorkPoints
			If wp.Point.IsEqualTo(endPoint, DefaultTolerance) Then
				NeedToAddEndPointWP = False
				oEndPoint = wp
				oEndPoint.Visible = True 'in case it happens to be the origin point.
				Exit For
			End If
		Next
	End If

	If NeedToAddStartPointWP Then
		oStartPoint = oDef.WorkPoints.AddFixed(startPoint, WorkPointsForConstructionPurposesOnly)
		oStartPoint.Grounded = True
	End If
	WorkGeomColl.Add(oStartPoint)

	If NeedToAddEndPointWP Then
		oEndPoint = oDef.WorkPoints.AddFixed(endPoint, WorkPointsForConstructionPurposesOnly)
		oEndPoint.Grounded = True
	End If
	WorkGeomColl.Add(oEndPoint)
	
	Dim SelectedStartPoint As WorkPoint = oStartPoint 'ThisApplication.CommandManager.Pick(SelectionFilterEnum.kWorkPointFilter, "Select the " & SelectionPrompt & " startpoint (the point with the smallest Y value)")
	Dim SelectedEndPoint As WorkPoint = oEndPoint
	
	If Not WorkPointsForConstructionPurposesOnly Then
		oStartPoint.Name = oStartPoint.Name.Replace("Work Point", WorkPointPrefix & "StartPoint")
		oEndPoint.Name = oEndPoint.Name.Replace("Work Point", WorkPointPrefix & "EndPoint")
	End If
	
	If oDef.WorkAxes.Count > 3 Then '3 because X, Y & Z..?
		For Each wa As WorkAxis In oDef.WorkAxes
			If wa.DefinitionType = WorkAxisDefinitionEnum.kFixedWorkAxis
				'probably one we added earlier and need to either delete or reuse
				Dim waDef As FixedWorkAxisDef = wa.Definition
				If Not waDef Is Nothing Then
					If waDef.OriginPoint.IsEqualTo(oStartPoint.Point, DefaultTolerance) Or waDef.OriginPoint.IsEqualTo(oEndPoint.Point, DefaultTolerance) Then
						If waDef.Axis.IsEqualTo(LineDirection, DefaultTolerance) Then
							NeedToAddDirectionAxis = False
							oTempWorkAxis = wa
							Exit For
						End If
					End If
				End If
			End If
		Next
	End If

	If NeedToAddDirectionAxis Then
		oTempWorkAxis = oDef.WorkAxes.AddFixed(SelectedStartPoint.Point, LineDirection, WorkPointsForConstructionPurposesOnly)
		oTempWorkAxis.Grounded = "True"
		if not WorkPointsForConstructionPurposesOnly then
			oTempWorkAxis.AutoResize = "True"
			oTempWorkAxis.Name = oTempWorkAxis.Name.Replace("Work Axis", WorkPointPrefix & "Axis")
		end if
	End If

	WorkGeomColl.Add(oTempWorkAxis)
	
	Datum.AngleToXAxis = RadiansToDegrees(oTempWorkAxis.Line.Direction.AngleTo(oXAxis.Line.Direction))
	Datum.Direction = oTempWorkAxis.Line.Direction
	Datum.StartPoint = New Point3d(SelectedStartPoint.Point)
	Datum.EndPoint = New Point3d(SelectedEndPoint.Point)
	Datum.Length = Measure.MinimumDistance(SelectedStartPoint.Name, SelectedEndPoint.Name)
	
	'cleanup on aisle three:
	If WorkPointsForConstructionPurposesOnly Then
		For Each workGeom As Object In WorkGeomColl
			If TypeOf workGeom Is WorkPoint Then
				Try
					Dim wp As WorkPoint = workGeom
					wp.Delete()
				Catch
					Continue For
				End Try
			Else If TypeOf workGeom Is WorkAxis Then
				Try
					Dim wa As WorkAxis = workGeom
					wa.Delete()
				Catch
					Continue For
				End Try
			End If
		Next
	Else
		WorkGeomColl.Clear()
	End If
	
	Return Datum

End Function

Public Function RadiansToDegrees(ByVal radians As Double) As Double
    Return radians * kRadiansToDegrees
End Function

Public Function DegreesToRadians(ByVal degrees As Double) As Double
    Return degrees * kDegreesToRadians
End Function

Public Function GetRotatedPoint(ByVal OGPoint As Point, ByVal RotationAngle As Double) As Point
	Dim RotatedPoint As Point = ThisApplication.TransientGeometry.CreatePoint(0,0,0)
	RotatedPoint.X = OGPoint.X * Math.Cos(RotationAngle) - OGPoint.Y * Math.Sin(RotationAngle)
	RotatedPoint.Y = OGPoint.X * Math.Sin(RotationAngle) + OGPoint.Y * Math.Cos(RotationAngle)
	RotatedPoint.Z = OGPoint.Z
	Return RotatedPoint
End Function

Public Function PolarPoints(ByVal pPt As Point, ByVal dAng As Double, ByVal dDist As Double) As Point
    Dim X As Double = dDist * Math.Cos(dAng)
    Dim Y As Double = dDist * Math.Sin(dAng)
	Dim NewPoint As Point = ThisApplication.TransientGeometry.CreatePoint(pPt.X + X, pPt.Y + Y, pPt.Z)
	Return NewPoint
End Function

 

The attached AngleGoesBrrr.ipt contains an iLogic form to control the angles and I have begun to investigate how I might perform some automated testing on this because click, click, click "oh it's wrong again" gets VERY tedious, extremely quickly.

 

My plan for this is to use one of the Unit Testing Frameworks (either Microsoft's own, or NUnit) because in the part file I have "labelled" the lines along with the always-correct Sketch point using @ekinsb's extremely useful "Nifty Attributes" addin, such that I can programmatically select both lines, run my code and test the resulting point against the position of the known-good sketch point.

 

I'll probably throw that up as a separate forum post when I get to it, but for now, I would love for someone to look at the code ^ and say, "throw that away and do it like {x} instead"

 

If you do download these files, the rule above and it's three classes are external rules. Without placing them in your External rules folder, the second button on the form will appear with a strikethrough. (the first button I haven't provided code for)

 

Thanks,

 

Alex

 

 

*I had overlooked this CRITICAL requirement when I threw together this part file late last week and subsequently spent most of Thursday & ALL of Friday fighting with Inventor's API and wondering why I couldn't get angles more than 180° apart for the two lines when I measured them in-code. Cue, me realising my mistake as I was walking home on Friday evening. Ho-hum.

0 Likes
392 Views
0 Replies
Replies (0)