Imports Inventor

Public Class Form1
    Private m_invApp As Inventor.Application = Nothing
    Private m_asmDoc As AssemblyDocument = Nothing
    Private m_drawDoc As DrawingDocument = Nothing
    Private m_eventDrawview As DrawingView = Nothing
    Private WithEvents m_drawViewEvents As DrawingViewEvents = Nothing



    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            m_invApp = GetObject(, "Inventor.Application")
        Catch ex As Exception
            MsgBox("Inventor must be running.")
            Me.Close()
        End Try
    End Sub


    Private Sub btnCreateDrawing_Click(sender As Object, e As EventArgs) Handles btnCreateDrawing.Click
        If m_invApp.ActiveDocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then
            MsgBox("The assembly to create a layout for must be active")
            Return
        End If
        m_asmDoc = m_invApp.ActiveDocument

        m_invApp.ScreenUpdating = False
        m_invApp.UserInterfaceManager.UserInteractionDisabled = True

        ' Create a new drawing.
        m_drawDoc = m_invApp.Documents.Add(DocumentTypeEnum.kDrawingDocumentObject, m_invApp.FileManager.GetTemplateFile(DocumentTypeEnum.kDrawingDocumentObject, True))

        ' Get the initial default sheet.
        Dim startSheet As Sheet = m_drawDoc.Sheets.Item(1)

        ' Create a new sheet.
        Dim layoutSheet As Sheet = m_drawDoc.Sheets.Add(DrawingSheetSizeEnum.kCDrawingSheetSize, PageOrientationTypeEnum.kLandscapePageOrientation, "Layout")

        ' Add the border and title block.
        layoutSheet.AddDefaultBorder()
        layoutSheet.AddTitleBlock(m_drawDoc.TitleBlockDefinitions.Item("ANSI - Large"))

        ' Delete the initial sheet.
        startSheet.Delete()

        ' Get the overall size of the assembly to determine the view scale.
        Dim asmBounds As Box = m_asmDoc.ComponentDefinition.RangeBox
        Dim modelWidth As Double = asmBounds.MaxPoint.X - asmBounds.MinPoint.X
        Dim modelHeight As Double = asmBounds.MaxPoint.Y - asmBounds.MinPoint.Y

        Dim spacing As Double = 2
        Dim borderBounds As Box2d = layoutSheet.Border.RangeBox
        Dim tbBounds As Box2d = layoutSheet.TitleBlock.RangeBox
        Dim drawWidth As Double = borderBounds.MaxPoint.X - borderBounds.MinPoint.X - (2 * spacing)
        Dim drawHeight As Double = borderBounds.MaxPoint.Y - tbBounds.MaxPoint.Y - (2 * spacing)

        Dim scale As Double
        scale = drawWidth / modelWidth
        If drawHeight / modelHeight < scale Then
            scale = drawHeight / modelHeight
        End If

        ' Determine the center of the view.
        Dim tg As TransientGeometry = m_invApp.TransientGeometry
        Dim center As Point2d
        center = tg.CreatePoint2d((borderBounds.MinPoint.X + borderBounds.MaxPoint.X) / 2,
                                  (borderBounds.MaxPoint.Y + tbBounds.MaxPoint.Y) / 2)

        ' Create the draft view.
        Dim draftView As DrawingView
        draftView = layoutSheet.DrawingViews.AddAssociativeDraftView(m_asmDoc, center, scale, "Layout")

        ' A sketch is automatically created and activated, so get it.
        Dim viewSketch As DrawingSketch = draftView.Sketches.Item(1)

        ' Draw the shapes for each occurrence.
        DrawShapes(m_asmDoc, viewSketch)

        ' Exit the edit mode of the sketch.
        viewSketch.ExitEdit()

        ' Reposition the view because it doesn't stay where it was created.
        draftView.Center = center

        ' Connect the event.
        m_drawViewEvents = draftView.DrawingViewEvents
        m_eventDrawview = draftView

        m_invApp.ScreenUpdating = True
        m_invApp.UserInterfaceManager.UserInteractionDisabled = False
    End Sub


    Private Sub btnConnectEvents_Click(sender As Object, e As EventArgs) Handles btnConnectEvents.Click
        If m_invApp.ActiveDocumentType <> DocumentTypeEnum.kDrawingDocumentObject Then
            MsgBox("A drawing must be active")
            Return
        End If

        Dim drawDoc As DrawingDocument = m_invApp.ActiveDocument
        m_eventDrawview = drawDoc.ActiveSheet.DrawingViews.Item(1)
        m_drawViewEvents = m_eventDrawview.DrawingViewEvents
    End Sub


    Private Sub m_drawViewEvents_OnViewUpdate(BeforeOrAfter As EventTimingEnum, Context As NameValueMap, ReasonsForChange As CommandTypesEnum, ByRef HandlingCode As HandlingCodeEnum) Handles m_drawViewEvents.OnViewUpdate
        ' Get the associated assembly document.
        Dim asmDoc As AssemblyDocument = m_eventDrawview.ReferencedFile.ReferencedDocument

        DoUpdate(asmDoc, m_eventDrawview)
    End Sub


    Private Sub DrawShapes(asmDoc As AssemblyDocument, drawSketch As DrawingSketch)
        Dim tg As TransientGeometry = m_invApp.TransientGeometry

        For Each occ As Inventor.ComponentOccurrence In asmDoc.ComponentDefinition.Occurrences
            ' Get the bounds of the occurrence.
            Dim bounds As Box = occ.RangeBox

            ' Draw a rectangle.
            Dim resultLines As SketchEntitiesEnumerator
            resultLines = drawSketch.SketchLines.AddAsTwoPointRectangle(tg.CreatePoint2d(bounds.MinPoint.X, bounds.MinPoint.Y),
                                                                        tg.CreatePoint2d(bounds.MaxPoint.X, bounds.MaxPoint.Y))

            ' Fill the rectangle.
            Dim tObjs As TransientObjects = m_invApp.TransientObjects
            Dim prof As Profile = drawSketch.Profiles.AddForSolid(False, tObjs.CreateObjectCollection(resultLines))
            drawSketch.SketchFillRegions.Add(prof, tObjs.CreateColor(150, 220, 235))

            ' Add a label.
            Dim center As Point2d
            center = tg.CreatePoint2d((bounds.MinPoint.X + bounds.MaxPoint.X) / 2,
                                      (bounds.MinPoint.Y + bounds.MaxPoint.Y) / 2)
            Dim skText As TextBox = drawSketch.TextBoxes.AddFitted(center, occ.Name)
            skText.VerticalJustification = VerticalTextAlignmentEnum.kAlignTextMiddle
            skText.HorizontalJustification = HorizontalTextAlignmentEnum.kAlignTextCenter

            ' Add attribute for the cell.
            Dim newNum As Integer = GetNextNumber(m_drawDoc)
            Dim layoutSet As AttributeSet = m_drawDoc.AttributeSets.Item("ekinsLayout")

            ' Build up the data for the cell.
            Dim cellData As String = GetCellData(occ)

            ' Add an attribute for the cell to the drawing.
            Dim layoutAttrib As Attribute = layoutSet.Add("CellOcc" & newNum.ToString, ValueTypeEnum.kStringType, cellData)

            ' Add an attribute to each line.
            Dim attribSet As AttributeSet
            For Each skLine As SketchLine In resultLines
                attribSet = skLine.AttributeSets.Add("ekinsLayout")
                attribSet.Add("Cell_Line", ValueTypeEnum.kIntegerType, newNum)
            Next

            attribSet = skText.AttributeSets.Add("ekinsLayout")
            attribSet.Add("Cell_Text", ValueTypeEnum.kIntegerType, newNum)
        Next
    End Sub

    Private Function GetCellData(occ As ComponentOccurrence) As String
        Dim attribVal As String
        Dim refKey As Byte() = {}
        occ.GetReferenceKey(refKey)
        Dim asmDoc As AssemblyDocument = occ.Parent.Document
        attribVal = asmDoc.ReferenceKeyManager.KeyToString(refKey)
        attribVal &= "||" & PositionInfo(occ.RangeBox) & "||" & occ.Name

        Return attribVal
    End Function

    Private Function PositionInfo(boundBox As Box) As String
        Dim retVal As String
        retVal = String.Format("{0,0:F6};{1,0:F4};{2,0:F4};{3,0:F4}", boundBox.MinPoint.X, boundBox.MinPoint.Y, boundBox.MaxPoint.X, boundBox.MaxPoint.Y)
        Return retVal
    End Function

    Private Function GetNextNumber(doc As Document) As Integer
        Dim attribSet As AttributeSet
        If doc.AttributeSets.NameIsUsed("ekinsLayout") Then
            attribSet = doc.AttributeSets.Item("ekinsLayout")
        Else
            attribSet = doc.AttributeSets.Add("ekinsLayout")
        End If

        If attribSet.NameIsUsed("MaxCount") Then
            Dim attrib As Attribute = attribSet.Item("MaxCount")
            Dim value As Integer = attrib.Value
            value += 1
            attrib.Value = value
            Return value
        Else
            attribSet.Add("MaxCount", ValueTypeEnum.kIntegerType, 0)
            Return 0
        End If
    End Function

    Private Sub btnDoUpdate_Click(sender As Object, e As EventArgs) Handles btnDoUpdate.Click
        If m_invApp.ActiveDocumentType <> DocumentTypeEnum.kDrawingDocumentObject Then
            MsgBox("A layout drawing must be active.")
            Return
        End If

        ' Get the first view on the active sheet.  For a layout drawing there should just be one
        ' sheet which contains the single layout view.
        Dim drawDoc As DrawingDocument = m_invApp.ActiveDocument
        Dim sht As Sheet = drawDoc.ActiveSheet
        Dim drawView As DrawingView = sht.DrawingViews.Item(1)

        ' Get the associated assembly document.
        Dim asmDoc As AssemblyDocument = drawView.ReferencedFile.ReferencedDocument

        DoUpdate(asmDoc, drawView)
    End Sub


    Private Sub DoUpdate(asmDoc As AssemblyDocument, drawView As DrawingView)
        Dim drawDoc As DrawingDocument = drawView.Parent.Parent

        m_invApp.ScreenUpdating = False
        m_invApp.UserInterfaceManager.UserInteractionDisabled = True

        ' Get the cell information from the drawing.
        Dim cellAttribs As AttributesEnumerator = drawDoc.AttributeManager.FindAttributes("ekinsLayout", "CellOcc*")

        Dim isSketchInEdit As Boolean = False
        Dim sk As DrawingSketch = Nothing
        For Each attrib As Attribute In cellAttribs
            Dim attribVal As String = attrib.Value
            Dim parts() As String = Split(attribVal, "||")
            Dim refKeyString As String = parts(0)
            Dim positionString As String = parts(1)
            Dim occName As String = parts(2)
            Dim refKeyBytes() As Byte = {}
            drawDoc.ReferenceKeyManager.StringToKey(refKeyString, refKeyBytes)

            ' Get the occurrence in the asseembly using the reference key.
            Dim occ As ComponentOccurrence = asmDoc.ReferenceKeyManager.BindKeyToObject(refKeyBytes)

            Dim isOutOfDate = False
            If PositionInfo(occ.RangeBox) <> positionString Then
                isOutOfDate = True
            End If

            If occName <> occ.Name Then
                isOutOfDate = True
            End If

            If isOutOfDate Then
                ' Get the sketch points from the lines starting with the min point and
                ' then around the rectangle in clockwise order.
                Dim rectPoints() As SketchPoint = GetLinePoints(drawDoc, attrib.Name)

                If Not isSketchInEdit Then
                    ' Activate the sketch.
                    sk = rectPoints(0).Parent
                    sk.Edit()
                    isSketchInEdit = True
                End If

                ' Move the points.
                Dim occBounds As Box = occ.RangeBox
                Dim tg As TransientGeometry = m_invApp.TransientGeometry
                rectPoints(0).MoveTo(tg.CreatePoint2d(occBounds.MinPoint.X, occBounds.MinPoint.Y))
                rectPoints(1).MoveTo(tg.CreatePoint2d(occBounds.MinPoint.X, occBounds.MaxPoint.Y))
                rectPoints(2).MoveTo(tg.CreatePoint2d(occBounds.MaxPoint.X, occBounds.MaxPoint.Y))
                rectPoints(3).MoveTo(tg.CreatePoint2d(occBounds.MaxPoint.X, occBounds.MinPoint.Y))

                ' Update the text.
                Dim foundEnts As ObjectsEnumerator = drawDoc.AttributeManager.FindObjects("ekinsLayout", "Cell_Text", Integer.Parse(attrib.Name.Substring(7)))
                If foundEnts.Count > 0 Then
                    Dim text As TextBox = foundEnts.Item(1)
                    text.Text = occ.Name

                    Dim center As Point2d
                    center = tg.CreatePoint2d((occBounds.MinPoint.X + occBounds.MaxPoint.X) / 2,
                                              (occBounds.MinPoint.Y + occBounds.MaxPoint.Y) / 2)

                    text.Origin = center
                End If

                ' Update the attribute information.
                attrib.Value = GetCellData(occ)
            End If
        Next

        If isSketchInEdit Then
            sk.ExitEdit()
        End If

        m_invApp.ScreenUpdating = True
        m_invApp.UserInterfaceManager.UserInteractionDisabled = False
    End Sub


    Private Function GetLinePoints(drawDoc As DrawingDocument, cellName As String) As Inventor.SketchPoint()
        ' Find the lines, using the cell name.
        Dim cellNum As Integer = Integer.Parse(cellName.Substring(7))
        Dim foundLines As ObjectsEnumerator = drawDoc.AttributeManager.FindObjects("ekinsLayout", "Cell_Line", cellNum)

        ' Get the four points and order them
        Dim points(3) As SketchPoint
        Dim pointCount As Integer = 0
        For Each foundLine As SketchLine In foundLines
            If pointCount = 0 Then
                points(0) = foundLine.StartSketchPoint
                points(1) = foundLine.EndSketchPoint
                pointCount = 2
            Else
                Dim found As Boolean = False
                For i As Integer = 0 To pointCount - 1
                    If points(i) Is foundLine.StartSketchPoint Then
                        found = True
                    End If
                Next

                If Not found Then
                    points(pointCount) = foundLine.StartSketchPoint
                    pointCount += 1
                End If

                If pointCount = 4 Then
                    Exit For
                End If

                found = False
                For i As Integer = 0 To pointCount - 1
                    If points(i) Is foundLine.EndSketchPoint Then
                        found = True
                    End If
                Next

                If Not found Then
                    points(pointCount) = foundLine.EndSketchPoint
                    pointCount += 1
                End If

                If pointCount = 4 Then
                    Exit For
                End If
            End If
        Next

        ' Reorder the points so the minimum point is the first and then CCW order.
        points = points.OrderBy(Function(pnt) pnt.Geometry.X).ToArray
        If points(1).Geometry.Y < points(0).Geometry.Y Then
            Dim tmp As SketchPoint = points(0)
            points(0) = points(1)
            points(1) = tmp
        End If

        If points(3).Geometry.Y > points(2).Geometry.Y Then
            Dim tmp As SketchPoint = points(2)
            points(2) = points(3)
            points(3) = tmp
        End If

        ' Return the points.
        Return points
    End Function

    Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
        Me.Close()
    End Sub
End Class
