Positioning Assembly Occurrences

Rob Cohee recently posted a video demonstrating some skeletal modeling techniques in Inventor related to .  To simplify the process he used a VBA macro that I wrote for him.  It’s a simple macro and is useful by itself but also helps to demonstrate some API capabilities.  Here’s the macro that I provided Rob.

Public Sub AlignOccurrencesWithConstraints()
    Dim assemblydoc As AssemblyDocument
    Set assemblydoc = ThisApplication.ActiveDocument

    ' Get the occurrences in the select set.
    Dim occurrenceList As New Collection
    Dim entity As Object
    For Each entity In assemblydoc.SelectSet
        If TypeOf entity Is ComponentOccurrence Then
            occurrenceList.Add entity
        End If
    Next

    If occurrenceList.Count < 2 Then
        MsgBox "At least two occurrences must be selected."
        Exit Sub
    End If

    ' This assumes the first selected occurrence is the "base"
    ' and will constrain the base workplanes of all the other parts
    ' to the base workplanes of the first part. If there are
    ' constraints on the other they end up being over constrained.

    ' Get the planes from the base part and create proxies for them.
    Dim baseOccurrence As ComponentOccurrence
    Set baseOccurrence = occurrenceList.Item(1)

    Dim BaseXY As WorkPlane
    Dim BaseYZ As WorkPlane
    Dim BaseXZ As WorkPlane
    Call GetPlanes(baseOccurrence, BaseXY, BaseYZ, BaseXZ)

    Dim constraints As AssemblyConstraints
    Set constraints = assemblydoc.ComponentDefinition.constraints

    ' Iterate through the other occurrences
    Dim i As Integer
    For i = 2 To occurrenceList.Count
        Dim thisOcc As ComponentOccurrence
        Set thisOcc = occurrenceList.Item(i)

        ' Move it to the base occurrence so that if the base is
        ' not fully constrained it shouldn't move when the flush
        ' constraints are added.
        thisOcc.Transformation = baseOccurrence.Transformation

        ' Get the planes from the occurrence
        Dim occPlaneXY As WorkPlane
        Dim occPlaneYZ As WorkPlane
        Dim occPlaneXZ As WorkPlane
        Call GetPlanes(thisOcc, occPlaneXY, occPlaneYZ, occPlaneXZ)

        ' Add the flush constraints.
        Call constraints.AddFlushConstraint(BaseXY, occPlaneXY, 0)
        Call constraints.AddFlushConstraint(BaseYZ, occPlaneYZ, 0)
        Call constraints.AddFlushConstraint(BaseXZ, occPlaneXZ, 0)
    Next
End Sub

' Utility function used by the AlignOccurrencesWithConstraints macro.
' Given an occurrence it returns the base work planes that are in
' the part or assembly the occurrence references.  It gets the
' proxies for the planes since it needs the work planes in the
' context of the assembly and not in the part or assembly document
' where they actually exist.

Private Sub GetPlanes(ByVal Occurrence As ComponentOccurrence, _ 
                      ByRef BaseXY As WorkPlane, _ 
                      ByRef BaseYZ As WorkPlane, _ 
                      ByRef BaseXZ As WorkPlane)
    ' Get the work planes from the definition of the occurrence.
    ' These will be in the context of the part or subassembly, not 
    ' the top-level assembly, which is what we need to return.
    Set BaseXY = Occurrence.Definition.WorkPlanes.Item(3)
    Set BaseYZ = Occurrence.Definition.WorkPlanes.Item(1)
    Set BaseXZ = Occurrence.Definition.WorkPlanes.Item(2)

    ' Create proxies for these planes.  This will act as the work
    ' plane in the context of the top-level assembly.
    Call Occurrence.CreateGeometryProxy(BaseXY, BaseXY)
    Call Occurrence.CreateGeometryProxy(BaseYZ, BaseYZ)
    Call Occurrence.CreateGeometryProxy(BaseXZ, BaseXZ)
End Sub

The program above constrains the base work planes of the set of selected occurrences to the base work planes of one other selected occurrence (the base part).  It places three flush constraints between each part and the base part.  The advantage to this is that the occurrences are all constrained to the base part so that if the base part is repositioned all of the other parts will automatically move to maintain the relationship defined by the constraints.  The disadvantage is that it’s doing quite a bit of work and each constraint placement causes the assembly to recompute, which is what gave Rob time to go get coffee.

There’s another approach that is much faster if you just need to position parts but don’t need to associate their position to another part.  The macro below does this.  It’s simpler and runs much faster.  This macro positions the parts exactly as the previous macro but instead of creating constraints it just repositions them and then grounds them so they can’t be accidentally moved.

Public Sub AlignOccurrencesWithGround()
    Dim assemblydoc As AssemblyDocument
    Set assemblydoc = ThisApplication.ActiveDocument

    ' Get the occurrences in the select set.  The first occurrence
    ' selected will be the "base" where all of the other occurrences
    ' will be positioned relative to it.
    Dim baseOccurrence As ComponentOccurrence
    Dim baseTransform As Matrix
    Dim transObjects As TransientObjects
    Set transObjects = ThisApplication.TransientObjects
    Dim occList As ObjectCollection
    Set occList = transObjects.CreateObjectCollection
    Dim transformList As ObjectCollection
    Set transformList = transObjects.CreateObjectCollection
    Dim entity As Object
    For Each entity In assemblydoc.SelectSet
        ' Check that the selected entity is an occurrence.
        If TypeOf entity Is ComponentOccurrence Then
            ' Check if the base occurrence has been assigned.
            ' If not then this is the first occurrence found and
            ' use it as the base occurrence.
            If baseOccurrence Is Nothing Then
                Set baseOccurrence = entity
                Set baseTransform = baseOccurrence.Transformation
            Else
                ' This is another selected occurrence so just
                ' add it to the list.
                occList.Add entity
                transformList.Add baseTransform
            End If
        End If
    Next 

    ' Check that at least a base occurrence and one other occurrence
    ' was selected.
    If occList.Count < 1 Then
        MsgBox "At least two occurrences must be selected."
        Exit Sub
    End If

    ' Reposition all of the occurrences.  The TransformOccurrences
    ' method was new in Inventor 2009.
    Dim assemblyDef As AssemblyComponentDefinition
    Set assemblyDef = assemblydoc.ComponentDefinition
    Call assemblyDef.TransformOccurrences( occList, _
                                           transformList)

    ' Iterate through the occurrences and ground them.
    Dim i As Integer
    For i = 1 To occList.Count
        Dim thisOccurrence As ComponentOccurrence
        Set thisOccurrence = occList.Item(i)
        thisOccurrence.Grounded = True
    Next
End Sub

Here’s one final version of this macro that is still even simpler.  In the two previous macros the selected occurrences were moved relative to the location of the first selected occurrence.  The macro below moves every occurrence to be positioned relative to the base assembly coordinate system.  It doesn’t rely on selection but repositions every occurrence and grounds it.

Public Sub AlignOccurrencesWithOrigin()
    Dim assemblyDoc As AssemblyDocument
    Set assemblyDoc = ThisApplication.ActiveDocument
    Dim assemblyDef As AssemblyComponentDefinition
    Set assemblyDef = assemblyDoc.ComponentDefinition

    ' Create a matrix.  It is initialized as an identity matrix
    ' which means it defines a position as the origin and aligned
    ' with the global x, y, and z axes.
    Dim transGeom As TransientGeometry
    Set transGeom = ThisApplication.TransientGeometry
    Dim baseTransform As Matrix
    Set baseTransform = transGeom.CreateMatrix

    ' Create collections to load the occurrences into.
    Dim transObjects As TransientObjects
    Set transObjects = ThisApplication.TransientObjects
    Dim occList As ObjectCollection
    Set occList = transObjects.CreateObjectCollection
    Dim transformList As ObjectCollection
    Set transformList = transObjects.CreateObjectCollection

    ' Iterate through all of the occurrences.
    Dim occurrence As ComponentOccurrence
    For Each occurrence In assemblyDef.Occurrences
        ' Add each occurrence to the list.
        occList.Add occurrence

        ' Add the transform to the list.
        transformList.Add baseTransform

        ' Ground each occurrence.  This is ok to do here
        ' because the move will ignore the ground condition.
        occurrence.Grounded = True
    Next

    ' Reposition all of the occurrences.  The TransformOccurrences
    ' method was new in Inventor 2009. 
    Set assemblyDef = assemblydoc.ComponentDefinition
    Call assemblyDef.TransformOccurrences(occList, _
                                           transformList)
End Sub

Of course these macros just the demonstrate one solution using the API to a specific problem.  The same functionality demonstrated above and combined with other API functions can be used in many other ways to solve other problems.  All I’m trying to say is that if the macros above don’t quite do what you need them to do it’s time to start investigating Inventor’s API so that you can modify them to solve your specific problems.


Comments

4 responses to “Positioning Assembly Occurrences”

  1. Brian Ekins Avatar
    Brian Ekins

    I added one more sample to the post. It’s the last of the three and is simpler and is likely to be the functionality that more people will want.

  2. Tore Marskar Eek-Jensen Avatar
    Tore Marskar Eek-Jensen

    Hi Brian
    Great blog. I have a question. Previously we have flushed components to origin and avoided using grouding. What would happen if I ground a component also flushed to origin. Would Inventor skip computing flush constaints sinse it’s already fixed?
    Cheers
    Tore

  3. Hi Tore,
    Good question and the truth is that I don’t know the answer for sure but I believe that Inventor will optimize the solve and recognizes that it can ignore any grounded components.
    -Brian

  4. Hi Brian
    I’m using inventor 2008. In assembly i’m using more than 100 LOD’s .At this point if add one part to this assembly.. that appears in all the existing LOD’s…. what would you suggest to get rid of additional parts from the existing LOD’s by default…..

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading