Automating Dimension Placement in Inventor Drawings




When working with multiple variants of a part, creating drawings for each variation can be tedious and repetitive. By cleverly using Inventorโ€™s attribute system in conjunction with custom code, it is possible to fully automate the creation of drawings. In the following example, we will focus on placing dimensions, as these are often the most complicated part. However, the principles demonstrated here can be applied to any part of the drawing.

Attributes

To understand the following code, we first need to know the basics of what attributes are. In short, attributes are a way to attach data to almost any element of an Inventor file. This can allow us to add tags to geometry, making it easy for the code to find and interact with these elements.

Attributes use a folder-like system known as sets. Each set is attached to an element of the document and contains individual attributes. To view or edit attributes, it is recommended to install Attribute Helper, which is part of the user tools package for Inventor. For installation or fixing Attribute Helper, check out this page: Fixing Attribute Helper.

To add or edit attributes in a part, you will need either Attribute Helper or your own custom tool, as Inventor does not provide a way to access attributes in the user interface.

Automating Dimension Placement

To automate the dimensioning process, we first need a parametric part or assembly with some custom attributes.

By attaching attribute sets to the edges of the part in pairs, we can create tags that the code can read to find the part’s geometry, convert it to a line on the drawing, and then add a dimension to it. Within the sets, we will have a master that contains all the additional information about the dimension and a slave.

Each attribute set will need to contain the intent about the geometry it is attached to. This intent is the value for PointIntentEnum, which tells the dimension to attach to different areas of the line, such as the start, end, or middle. The full list of enums can be found here: PointIntentEnum Documentation. Note that not all enums are valid for every edge.

The master set will also contain an offset and an ‘ignore if’ list. The offset positions the dimension text so that it doesnโ€™t overlap with the part or another dimension. The ‘ignore if’ list contains numbers corresponding to other dimensions; if one of the dimensions in the list is due to be placed, the code will ignore this dimension to prevent overlaps.


The Code

Dim Attributes Struct

This struct encapsulates the necessary attributes and methods for managing dimension attributes:

C#
public static Inventor.Application _invApp;

private struct DimAttributes
{
    public string num;
    public AttributeSet AttributeSetM;
    public AttributeSet AttributeSetS;
    public Sheet oSheet;
    public DrawingView oView;
    public DrawingCurvesEnumerator[] Curves;
    public bool GeometryGood;
    public string[] IgnoreIf;

    // Constructor
    public DimAttributes(AttributeSet attributeSetM, AttributeSet attributeSetS, Sheet sheet)
    {
        AttributeSetM = attributeSetM;
        AttributeSetS = attributeSetS;
        num = ExtractNumberFromName(attributeSetM.Name);
        oSheet = sheet;
        oView = oSheet.DrawingViews[1];

        // Initialize IgnoreIf array by splitting the "IgnoreIf" attribute value
        string s = AttributeSetM["IgnoreIf"].Value;
        IgnoreIf =  s.Split(new char[] { ',' });

        Curves = new DrawingCurvesEnumerator[2];
        GeometryGood = true;
        try
        {
            Curves[0] = oView.DrawingCurves[AttributeSetM.Parent.Parent];
            Curves[1] = oView.DrawingCurves[AttributeSetS.Parent.Parent];
            if (Curves[0].Count != 1 && Curves[1].Count != 1) GeometryGood = false;
        }
        catch { GeometryGood = false; }
    }

    // Method to get a point based on the view's position and offset
    public Point2d GetPoint()
    {
        Point2d point = oView.Position;
        point.Y = point.Y - (oView.Height / 2) - AttributeSetM["Offset"].Value;
        return point;
    }

    // Method to get the intent enum based on whether it is master or not
    private PointIntentEnum GetIntentEnum(bool master)
    {
        long intentValue = master ? (long)AttributeSetM["Intent"].Value : (long)AttributeSetS["Intent"].Value;
        return (PointIntentEnum)intentValue;
    }

    // Method to create a geometry intent
    public GeometryIntent GetIntent(bool master) => oSheet.CreateGeometryIntent(Curves[master ? 0 : 1][1], GetIntentEnum(master));

}
VB
Public _invApp As Inventor.Application

Private Structure DimAttributes
    Public num As String
    Public AttributeSetM As AttributeSet
    Public AttributeSetS As AttributeSet
    Public oSheet As Sheet
    Public oView As DrawingView
    Public Curves As DrawingCurvesEnumerator()
    Public GeometryGood As Boolean
    Public IgnoreIf As String()

    ' Constructor
    Public Sub New(oAttributeSetM As AttributeSet, oAttributeSetS As AttributeSet, sheet As Sheet)
        AttributeSetM = oAttributeSetM
        AttributeSetS = oAttributeSetS
        num = ExtractNumberFromName(AttributeSetM.Name)
        oSheet = sheet
        oView = oSheet.DrawingViews(1)

        ' Initialize IgnoreIf array by splitting the "IgnoreIf" attribute value
        Dim s As String = AttributeSetM("IgnoreIf").Value
        IgnoreIf = s.Split(New Char() {","c})

        Curves = New DrawingCurvesEnumerator(1) {}
        GeometryGood = True
        Try
            Curves(0) = oView.DrawingCurves(AttributeSetM.Parent.Parent)
            Curves(1) = oView.DrawingCurves(AttributeSetS.Parent.Parent)
            If Curves(0).Count <> 1 AndAlso Curves(1).Count <> 1 Then
                GeometryGood = False
            End If
        Catch
            GeometryGood = False
        End Try
    End Sub

    ' Method to get a point based on the view's position and offset
    Public Function GetPoint() As Point2d
        Dim point As Point2d = oView.Position
        point.Y = point.Y - (oView.Height / 2) - AttributeSetM("Offset").Value
        Return point
    End Function

    ' Method to get the intent enum based on whether it is master or not
    Private Function GetIntentEnum(master As Boolean) As PointIntentEnum
        Dim intentValue As Long = If(master, AttributeSetM("Intent").Value, AttributeSetS("Intent").Value)
        Return CType(intentValue, PointIntentEnum)
    End Function

    ' Method to create a geometry intent
    Public Function GetIntent(master As Boolean) As GeometryIntent
        Return oSheet.CreateGeometryIntent(Curves(If(master, 0, 1))(1), GetIntentEnum(master))
    End Function
End Structure

By creating our own data structure to handle the attribute sets, we can easily store the pair of sets together along with handling any necessary data. The structure takes the two attribute sets and the sheet. It then stores the necessary data in an easy-to-access format and checks if the associated geometry exists and can be converted into curves on the drawing.

The structure also handles the conversion of curves into geometry intent. This combines the curve created from the attached geometry with the intent stored in the attribute to create a precise point on the geometry that the dimension can attach to.

Create Dims Method

This method acts as the entry point and is what handles the placement of dimensions based on the attributes:

C#
public static void CreateDims()
{
    if (_invApp is null) { _invApp = GetInventor(); }

    // Get the active drawing document and part document
    DrawingDocument oDoc = (DrawingDocument)_invApp.ActiveDocument;
    PartDocument pDoc = (PartDocument)oDoc.ReferencedDocuments[1];

    // Find the attribute sets for DimM and DimS
    AttributeSetsEnumerator dimMSets = pDoc.AttributeManager.FindAttributeSets("DimM-*");
    AttributeSetsEnumerator dimSSets = pDoc.AttributeManager.FindAttributeSets("DimS-*");

    Sheet oSheet = oDoc.ActiveSheet;
    List<DimAttributes> attrs = new List<DimAttributes>();

    // Match DimM and DimS attribute sets based on their number suffix
    foreach (AttributeSet dimMAttr in dimMSets)
    {
        string dimMNumber = ExtractNumberFromName(dimMAttr.Name);
        foreach (AttributeSet dimSAttr in dimSSets)
        {
            if (dimMNumber == ExtractNumberFromName(dimSAttr.Name))
            {
                DimAttributes attr = new DimAttributes(dimMAttr, dimSAttr, oSheet);
                if (attr.GeometryGood)
                {
                    attrs.Add(attr);
                }
                break; // Exit inner loop once a match is found
            }
        }
    }

    // Remove existing dimensions
    ObjectCollection objects = oDoc.AttributeManager.FindObjects("AutoDim");
    foreach (GeneralDimension Dim in objects) { Dim.Delete(); }
    oDoc.AttributeManager.PurgeAttributeSets("AutoDim", false, out _);

    // Place new dimensions
    foreach (DimAttributes attr in attrs)
    {
        try
        {
            // Skip if the attribute is in the ignore list
            bool skip = attrs.Any(attr2 => attr.IgnoreIf.Contains(attr2.num));
            if (skip) { continue; }

            // Add a linear dimension
            LinearGeneralDimension oDim = oSheet.DrawingDimensions.GeneralDimensions.AddLinear(attr.GetPoint(), attr.GetIntent(true), attr.GetIntent(false));
            oDim.CenterText();
            oDim.AttributeSets.Add("AutoDim");
            oDim.AttributeSets["AutoDim"].Add("DimNo", ValueTypeEnum.kStringType, attr.num);
        }
        catch { Console.WriteLine("Error placing dim " + attr.num); }
    }
}
VB
Public Sub CreateDims()
    If _invApp Is Nothing Then
        _invApp = GetInventor()
    End If

    ' Get the active drawing document and part document
    Dim oDoc As DrawingDocument = CType(_invApp.ActiveDocument, DrawingDocument)
    Dim pDoc As PartDocument = CType(oDoc.ReferencedDocuments(1), PartDocument)

    ' Find the attribute sets for DimM and DimS
    Dim dimMSets As AttributeSetsEnumerator = pDoc.AttributeManager.FindAttributeSets("DimM-*")
    Dim dimSSets As AttributeSetsEnumerator = pDoc.AttributeManager.FindAttributeSets("DimS-*")

    Dim oSheet As Sheet = oDoc.ActiveSheet
    Dim attrs As New List(Of DimAttributes)()

    ' Match DimM and DimS attribute sets based on their number suffix
    For Each dimMAttr As AttributeSet In dimMSets
        Dim dimMNumber As String = ExtractNumberFromName(dimMAttr.Name)
        For Each dimSAttr As AttributeSet In dimSSets
            If dimMNumber = ExtractNumberFromName(dimSAttr.Name) Then
                Dim attr As New DimAttributes(dimMAttr, dimSAttr, oSheet)
                If attr.GeometryGood Then
                    attrs.Add(attr)
                End If
                Exit For ' Exit inner loop once a match is found
            End If
        Next
    Next

    ' Remove existing dimensions
    Dim objects As ObjectCollection = oDoc.AttributeManager.FindObjects("AutoDim")
    For Each aDim As GeneralDimension In objects
        aDim.Delete()
    Next
    oDoc.AttributeManager.PurgeAttributeSets("AutoDim", False, Nothing)

    ' Place new dimensions
    For Each attr As DimAttributes In attrs
        Try
            ' Skip if the attribute is in the ignore list
            Dim skip As Boolean = attrs.Any(Function(attr2) attr.IgnoreIf.Contains(attr2.num))
            If skip Then Continue For

            ' Add a linear dimension
            Dim oDim As LinearGeneralDimension = oSheet.DrawingDimensions.GeneralDimensions.AddLinear(attr.GetPoint(), attr.GetIntent(True), attr.GetIntent(False))
            oDim.CenterText()
            oDim.AttributeSets.Add("AutoDim")
            oDim.AttributeSets("AutoDim").Add("DimNo", ValueTypeEnum.kStringType, attr.num)
        Catch
            Debug.WriteLine("Error placing dim " & attr.num)
        End Try
    Next
End Sub

To create the link to inventor for _invApp, check out the code on this page: Connecting Visual studio to other applications

This code is expected to be run with a drawing with one base view of the part open. The code will take the active drawing and the first referenced part. In the part, it searches for any dimension attribute sets.

Once two groups of attribute sets have been created, the code iterates through each one to find any matching sets. If a match is found, we use the DinAttributes structure to store the pair and check if the geometry is good before adding them to a list.

With a list of attribute pairs, the code deletes all old dimensions in the drawing to prevent duplicates. It then adds new dimensions by iterating through each attribute set in the list. The code checks if the dimension should be ignored before using the data set up by the structure to create a dimension. Once created, the dimension text is centered, and an attribute is added so that it can be found next time the program runs. An attribute with the dimension number is also added for identification purposes using Attribute Helper.

Extract numbers method

This is a simple little method that takes the name of the attribute sets and returns just the attribute number.

C#
static private string ExtractNumberFromName(string name)
{
    int dashIndex = name.LastIndexOf('-');
    if (dashIndex != -1 && dashIndex < name.Length - 1)
    {
        return name.Substring(dashIndex + 1);
    }
    return null;
}
VB
Private Function ExtractNumberFromName(name As String) As String
    Dim dashIndex As Integer = name.LastIndexOf("-"c)
    If dashIndex <> -1 AndAlso dashIndex < name.Length - 1 Then
        Return name.Substring(dashIndex + 1)
    End If
    Return Nothing
End Function

With that, the program is ready to go. Ensure you have a drawing open with a base view of the part in its default rotation.


Conclusion

By applying this approach to any part or assembly with many variants, you can save time and reduce mistakes when creating documentation for each variant. This example focuses on dimensions, but the approach can be used for any part of the drawing, such as pages, views, or other annotations, allowing for fully automated drawing creation.

To show how quickly and easily Inventor can iterate through each variant, try adding the following code to control the CreateDims method:

C#
public static void run()
{
    if (_invApp is null) { _invApp = InventorStuff.GetInventor(); }

    DrawingDocument oDoc = (DrawingDocument)_invApp.ActiveDocument;
    PartDocument pDoc = (PartDocument)oDoc.ReferencedDocuments[1];

    // Length is in cm
    int[] ints = new[] { 100,150,200,250,300,350,400};

    foreach (double i in ints)
    {
        pDoc.ComponentDefinition.Parameters["Length"].Value = i;
        pDoc.Update();
        oDoc.ActiveSheet.Update();
        CreateDims();
        System.Threading.Thread.Sleep(1000);
    }
}
VB
Public Sub Run()
    If _invApp Is Nothing Then
        _invApp = GetInventor()
    End If

    Dim oDoc As DrawingDocument = CType(_invApp.ActiveDocument, DrawingDocument)
    Dim pDoc As PartDocument = CType(oDoc.ReferencedDocuments(1), PartDocument)

    ' Length is in cm
    Dim ints As Integer() = {100, 150, 200, 250, 300, 350, 400}

    For Each i As Double In ints
        pDoc.ComponentDefinition.Parameters("Length").Value = i
        pDoc.Update()
        oDoc.ActiveSheet.Update()
        CreateDims()
        System.Threading.Thread.Sleep(1000)
    Next
End Sub

Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *