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.
![](/wp-content/uploads/2024/07/Auto-Dim.gif)
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:
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));
}
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:
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); }
}
}
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.
static private string ExtractNumberFromName(string name)
{
int dashIndex = name.LastIndexOf('-');
if (dashIndex != -1 && dashIndex < name.Length - 1)
{
return name.Substring(dashIndex + 1);
}
return null;
}
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:
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);
}
}
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
Leave a Reply