The question of getting the size of a part frequently comes up and I wanted to spend some time discussing the existing functionality and other options. The discussion here applies to both Fusion 360 and Inventor (and probably most other CAD systems).
What is a Bounding Box?
The easiest way to get a size of a part is to get the bounding box (also called the range box) of the object. Let’s look at what a bounding box is by looking at some simple 2D examples first. A 2D bounding box is a rectangle that encompasses the object. Below is an example of a part (the shaded area) and it’s bounding rectangle (dashed lines). If we measured the size of this box I think we would all agree that it looks like an accurate measure of the overall size of the part.
A typical bounding box is defined using just two points; the minimum and maximum points. The picture below shows the two 2D points used to define the bounding box. A limitation with defining a bounding box in this way is that the rectangle is oriented such that it’s edges are always parallel to the x and y model axes.
Below is the same part that has been rotated and its bounding box is displayed. In this case the size of the bounding box probably isn’t what you wanted and in most cases is not particularly useful.
What you probably really wanted was this bounding box but that box can’t be defined by a simple two points and needs additional information to define the orientation.
Bounding boxes aren’t something specific to the Inventor and Fusion 360 API’s but are a basic concept in computer graphics. The usual intent of a bounding box is not to provide a tight fitting, orientated box around a part but instead to provide a very fast box that fits around a certain piece of geometry. The only thing guaranteed is that the object is completely contained within the box and there is no guarantee about how tight the box fits around the object. In many cases, like in the first example, the size of the range box is the same as the physical size of the part but there are cases when this is not the case. Below, on the left, is an example from Inventor where the bounding box is quite a bit larger than the visible part. At first glance this would seem to be a bug in the calculation but the bounding box is including the control polygon of the spline surface, as shown on the right. So it may not be what you want but it is arguably correct and is the simplest for Inventor to calculate.
Bounding boxes are intended to be used in more complex operations where you need to first determine if a particular object should be considered in a calculation. To determine if an object should be included you can often use it’s bounding box to see if it’s within the area to be calculated. This is a very simple and fast calculation that can be performed to determine if the object can be eliminated from the more complicated and costly computations. Operations with bounding boxes like this are also very simple. For example you can combine bounding boxes to create a new box that contains the originals.
The Inventor API has the Box and Box2d objects that are used to pass back the bounding box information and these objects also provide properties and methods to let you work with the box information. The Fusion 360 API has the BoundingBox3D and BoundingBox2D objects that provide the equivalent functionality. The most important feature of these objects is that they return the coordinates of the min and max points.
Getting Accurate Bounding Boxes
As discussed above, the bounding box returned by the Inventor RangeBox property and returned by the Fusion360 boundingBox property may not be what you need. Unfortunately, a bounding box that is always tight fitting to the visible graphics is not currently provided by either Inventor or Fusion 360. However, it is possible to calculate a very close approximation on your own using other API functionality. What I’m going to show you here will create a tight range box but it will still be oriented so it’s parallel to the world XYZ planes. The ability to automatically calculate a bounding box that is oriented to get the tightest fitting box possible is a very difficult problem that I haven’t seen a solution to. It’s something you could probably write a PhD paper on. The easiest approach to the orientation problem is to ask the user for help by lettin them define the coordinate system the bounding box should be calculated in, essentially defining the directions for the length, width and height. That’s possible but a little more than I wanted to go into in this post so I’ll limit this discussion to calculating a tight fitting bounding box that’s oriented parallel to the world XYZ planes.
To do this you can use a triangular mesh representation of the model and calculate your own bounding box by including all of the vertices of the mesh. The result will be as accurate as the mesh. In both Inventor and Fusion 360 you can calculate a mesh representation to any tolerance you want or you can get the existing mesh that Inventor or Fusion 360 is using for the display of the model. The actual model is made up of accurate smooth surfaces, but a mesh representation is created and used internally to display the model. Using the existing mesh is faster that calculating a new one but if accuracy is critical you might want to calculate a new mesh. The shape of the model will also affect the accuracy. A model made up entirely of planes will result in an exact mesh representation regardless of the tolerance of the mesh because the triangles exactly represent those faces. If a model has any curved surfaces, even as simple as a cylinder, then the mesh is an approximation. I’ve tried to illustrate this in the picture below. On the left are the original models where one model is made entirely of planes and the other has planes, cylinders, and a sphere. On the right is the triangular mesh representations of the two parts. The first model is an exact representation because the shape can be exactly represented by the triangles. However, the second model has curved surfaces and has to be approximated by the triangles. The accuracy of the bounding box you build will depend on their being a triangle vertex at the minimum and maximum X, Y, and Z sizes of the model. The more triangles, the more likely you’ll have vertices at those locations but it also means more processing for Inventor or Fusion to calculate the mesh and more processing for you to analyze the mesh to build the range box.
I’ve used the code similar to that below in a few programs I’ve posted previously. The calculateTightBoundingBox function takes in a body and optional tolerance and returns the tight fitting bounding box. If no tolerance is provided, the existing display mesh is used. There can be more than one display mesh because Inventor and Fusion will generate different meshes depending on how close you zoom into the model so the display always appears smooth. When using the display mesh, this function will use the highest quality display mesh that exists. If a tolerance is provided, then a new mesh is calculated using that tolerance. There is a test function for each that is used to test the calculateTightBoundingBox function. Samples below are provided in Python for Fusion 360 and VBA and Visual Basic for Inventor. Below is the result on the previous model where the bounding box returned by Inventor was much larger.
Fusion 360 Python
# Function to test the calculateTightBoundingBox function.
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
des = adsk.fusion.Design.cast(app.activeProduct)
bodySelect = ui.selectEntity('Select the body.', 'Bodies')
body = adsk.fusion.BRepBody.cast(bodySelect.entity)
# Call the function to get the tight bounding box.
bndBox= calculateTightBoundingBox(body)
# Draw the bounding box using a sketch.
sk = des.rootComponent.sketches.add(des.rootComponent.xYConstructionPlane)
lines = sk.sketchCurves.sketchLines
minXYZ = bndBox.minPoint
minXYmaxZ = adsk.core.Point3D.create(bndBox.minPoint.x, bndBox.minPoint.y, bndBox.maxPoint.z)
minXmaxYZ = adsk.core.Point3D.create(bndBox.minPoint.x, bndBox.maxPoint.y, bndBox.maxPoint.z)
minXZmaxY = adsk.core.Point3D.create(bndBox.minPoint.x, bndBox.maxPoint.y, bndBox.minPoint.z)
maxXYZ = bndBox.maxPoint
maxXYminZ = adsk.core.Point3D.create(bndBox.maxPoint.x, bndBox.maxPoint.y, bndBox.minPoint.z)
maxXZminY = adsk.core.Point3D.create(bndBox.maxPoint.x, bndBox.minPoint.y, bndBox.maxPoint.z)
maxXminYZ = adsk.core.Point3D.create(bndBox.maxPoint.x, bndBox.minPoint.y, bndBox.minPoint.z)
lines.addByTwoPoints(minXYZ, minXYmaxZ)
lines.addByTwoPoints(minXYZ, minXZmaxY)
lines.addByTwoPoints(minXZmaxY, minXmaxYZ)
lines.addByTwoPoints(minXYmaxZ, minXmaxYZ)
lines.addByTwoPoints(maxXYZ, maxXYminZ)
lines.addByTwoPoints(maxXYZ, maxXZminY)
lines.addByTwoPoints(maxXYminZ, maxXminYZ)
lines.addByTwoPoints(maxXZminY, maxXminYZ)
lines.addByTwoPoints(minXYZ, maxXminYZ)
lines.addByTwoPoints(minXYmaxZ, maxXZminY)
lines.addByTwoPoints(minXmaxYZ, maxXYZ)
lines.addByTwoPoints(minXZmaxY, maxXYminZ)
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# Calculates a tight bounding box around the input body. An optional
# tolerance argument is available. This specificies the tolerance in
# centimeters. If not provided the best existing display mesh is used.
def calculateTightBoundingBox(body, tolerance = 0):
try:
# If the tolerance is zero, use the best display mesh available.
if tolerance <= 0:
# Get the best display mesh available.
triMesh = body.meshManager.displayMeshes.bestMesh
else:
# Calculate a new mesh based on the input tolerance.
meshMgr = adsk.fusion.MeshManager.cast(body.meshManager)
meshCalc = meshMgr.createMeshCalculator()
meshCalc.surfaceTolerance = tolerance
triMesh = meshCalc.calculate()
# Calculate the range of the mesh.
smallPnt = adsk.core.Point3D.cast(triMesh.nodeCoordinates[0])
largePnt = adsk.core.Point3D.cast(triMesh.nodeCoordinates[0])
vertex = adsk.core.Point3D.cast(None)
for vertex in triMesh.nodeCoordinates:
if vertex.x < smallPnt.x:
smallPnt.x = vertex.x
if vertex.y < smallPnt.y:
smallPnt.y = vertex.y
if vertex.z < smallPnt.z:
smallPnt.z = vertex.z
if vertex.x > largePnt.x:
largePnt.x = vertex.x
if vertex.y > largePnt.y:
largePnt.y = vertex.y
if vertex.z > largePnt.z:
largePnt.z = vertex.z
# Create and return a BoundingBox3D as the result.
return(adsk.core.BoundingBox3D.create(smallPnt, largePnt))
except:
# An error occurred so return None.
return(None)
Inventor VBA
Public Sub TestTightBoundingBox()
' Have a body selected.
Dim body As SurfaceBody
Set body = ThisApplication.CommandManager.Pick(kPartBodyFilter, "Select the body.")
' Call the function to get the tight bounding box.
Dim bndBox As Box
Set bndBox = calculateTightBoundingBox(body)
' Draw the bounding box using a 3D sketch.
Dim partDoc As PartDocument
Set partDoc = ThisApplication.ActiveDocument
Dim sk As Sketch3D
Set sk = partDoc.ComponentDefinition.Sketches3D.Add()
Dim lines As SketchLines3D
Set lines = sk.SketchLines3D
Dim tg As TransientGeometry
Set tg = ThisApplication.TransientGeometry
Dim minXYZ As Point
Dim minXYmaxZ As Point
Dim minXmaxYZ As Point
Dim minXZmaxY As Point
Set minXYZ = bndBox.MinPoint
Set minXYmaxZ = tg.CreatePoint(bndBox.MinPoint.x, bndBox.MinPoint.y, bndBox.MaxPoint.Z)
Set minXmaxYZ = tg.CreatePoint(bndBox.MinPoint.x, bndBox.MaxPoint.y, bndBox.MaxPoint.Z)
Set minXZmaxY = tg.CreatePoint(bndBox.MinPoint.x, bndBox.MaxPoint.y, bndBox.MinPoint.Z)
Dim maxXYZ As Point
Dim maxXYminZ As Point
Dim maxXZminY As Point
Dim maxXminYZ As Point
Set maxXYZ = bndBox.MaxPoint
Set maxXYminZ = tg.CreatePoint(bndBox.MaxPoint.x, bndBox.MaxPoint.y, bndBox.MinPoint.Z)
Set maxXZminY = tg.CreatePoint(bndBox.MaxPoint.x, bndBox.MinPoint.y, bndBox.MaxPoint.Z)
Set maxXminYZ = tg.CreatePoint(bndBox.MaxPoint.x, bndBox.MinPoint.y, bndBox.MinPoint.Z)
Call lines.AddByTwoPoints(minXYZ, minXYmaxZ)
Call lines.AddByTwoPoints(minXYZ, minXZmaxY)
Call lines.AddByTwoPoints(minXZmaxY, minXmaxYZ)
Call lines.AddByTwoPoints(minXYmaxZ, minXmaxYZ)
Call lines.AddByTwoPoints(maxXYZ, maxXYminZ)
Call lines.AddByTwoPoints(maxXYZ, maxXZminY)
Call lines.AddByTwoPoints(maxXYminZ, maxXminYZ)
Call lines.AddByTwoPoints(maxXZminY, maxXminYZ)
Call lines.AddByTwoPoints(minXYZ, maxXminYZ)
Call lines.AddByTwoPoints(minXYmaxZ, maxXZminY)
Call lines.AddByTwoPoints(minXmaxYZ, maxXYZ)
Call lines.AddByTwoPoints(minXZmaxY, maxXYminZ)
End Sub
' Calculates a tight bounding box around the input body. An optional
' tolerance argument is available. This specificies the tolerance in
' centimeters. If not provided the best existing display mesh is used.
Public Function calculateTightBoundingBox(body As SurfaceBody, Optional Tolerance As Double = 0) As Box
On Error GoTo ErrorFound
Dim vertCount As Long
Dim facetCount As Long
Dim vertCoords() As Double
Dim normVectors() As Double
Dim vertInds() As Long
' If the tolerance is zero, use the best display mesh available.
If Tolerance <= 0 Then
' Get the best display mesh available.
Dim tolCount As Long
Dim tols() As Double
Call body.GetExistingFacetTolerances(tolCount, tols)
Dim i As Integer
Dim bestTol As Double
bestTol = tols(0)
For i = 1 To tolCount - 1
If tols(i) < bestTol Then
bestTol = tols(i)
End If
Next
Call body.GetExistingFacets(bestTol, vertCount, facetCount, vertCoords, normVectors, vertInds)
Else
' Calculate a new mesh based on the input tolerance.
Call body.CalculateFacets(Tolerance, vertCount, facetCount, vertCoords, normVectors, vertInds)
End If
Dim tg As TransientGeometry
Set tg = ThisApplication.TransientGeometry
' Calculate the range of the mesh.
Dim smallPnt As Point
Dim largePnt As Point
Set smallPnt = tg.CreatePoint(vertCoords(0), vertCoords(1), vertCoords(2))
Set largePnt = tg.CreatePoint(vertCoords(0), vertCoords(1), vertCoords(2))
For i = 1 To vertCount - 1
Dim vertX As Double
Dim vertY As Double
Dim vertZ As Double
vertX = vertCoords(i * 3)
vertY = vertCoords(i * 3 + 1)
vertZ = vertCoords(i * 3 + 2)
If vertX < smallPnt.x Then
smallPnt.x = vertX
End If
If vertY < smallPnt.y Then
smallPnt.y = vertY
End If
If vertZ < smallPnt.Z Then
smallPnt.Z = vertZ
End If
If vertX > largePnt.x Then
largePnt.x = vertX
End If
If vertY > largePnt.y Then
largePnt.y = vertY
End If
If vertZ > largePnt.Z Then
largePnt.Z = vertZ
End If
Next
' Create and return a Box as the result.
Set calculateTightBoundingBox = tg.CreateBox()
calculateTightBoundingBox.MinPoint = smallPnt
calculateTightBoundingBox.MaxPoint = largePnt
Exit Function
ErrorFound:
Set calculateTightBoundingBox = Nothing
Exit Function
End Function
Inventor Visual Basic (iLogic)
Public Sub TestTightBoundingBox()
Dim invApp As Inventor.Application = GetObject(, "Inventor.Application")
' Have a body selected.
Dim body As SurfaceBody
body = invApp.CommandManager.Pick(SelectionFilterEnum.kPartBodyFilter, "Select the body.")
' Call the function to get the tight bounding box.
Dim bndBox As Box = calculateTightBoundingBox(body)
' Draw the bounding box using a 3D sketch.
Dim partDoc As PartDocument = invApp.ActiveDocument
Dim sk As Sketch3D = partDoc.ComponentDefinition.Sketches3D.Add()
Dim lines As SketchLines3D = sk.SketchLines3D
Dim tg As TransientGeometry = invApp.TransientGeometry
Dim minXYZ As Point = bndBox.MinPoint
Dim minXYmaxZ As Point = tg.CreatePoint(bndBox.MinPoint.X, bndBox.MinPoint.Y, bndBox.MaxPoint.Z)
Dim minXmaxYZ As Point = tg.CreatePoint(bndBox.MinPoint.X, bndBox.MaxPoint.Y, bndBox.MaxPoint.Z)
Dim minXZmaxY As Point = tg.CreatePoint(bndBox.MinPoint.X, bndBox.MaxPoint.Y, bndBox.MinPoint.Z)
Dim maxXYZ As Point = bndBox.MaxPoint
Dim maxXYminZ As Point = tg.CreatePoint(bndBox.MaxPoint.X, bndBox.MaxPoint.Y, bndBox.MinPoint.Z)
Dim maxXZminY As Point = tg.CreatePoint(bndBox.MaxPoint.X, bndBox.MinPoint.Y, bndBox.MaxPoint.Z)
Dim maxXminYZ As Point = tg.CreatePoint(bndBox.MaxPoint.X, bndBox.MinPoint.Y, bndBox.MinPoint.Z)
lines.AddByTwoPoints(minXYZ, minXYmaxZ)
lines.AddByTwoPoints(minXYZ, minXZmaxY)
lines.AddByTwoPoints(minXZmaxY, minXmaxYZ)
lines.AddByTwoPoints(minXYmaxZ, minXmaxYZ)
lines.AddByTwoPoints(maxXYZ, maxXYminZ)
lines.AddByTwoPoints(maxXYZ, maxXZminY)
lines.AddByTwoPoints(maxXYminZ, maxXminYZ)
lines.AddByTwoPoints(maxXZminY, maxXminYZ)
lines.AddByTwoPoints(minXYZ, maxXminYZ)
lines.AddByTwoPoints(minXYmaxZ, maxXZminY)
lines.AddByTwoPoints(minXmaxYZ, maxXYZ)
lines.AddByTwoPoints(minXZmaxY, maxXYminZ)
End Sub
' Calculates a tight bounding box around the input body. An optional
' tolerance argument is available. This specificies the tolerance in
' centimeters. If not provided the best existing display mesh is used.
Public Function calculateTightBoundingBox(body As SurfaceBody, Optional Tolerance As Double = 0) As Box
Try
Dim vertCount As Integer
Dim facetCount As Integer
Dim vertCoords() As Double = {}
Dim normVectors() As Double = {}
Dim vertInds() As Integer = {}
' If the tolerance is zero, use the best display mesh available.
If Tolerance <= 0 Then
' Get the best display mesh available.
Dim tolCount As Long
Dim tols() As Double = {}
Call body.GetExistingFacetTolerances(tolCount, tols)
Dim bestTol As Double
bestTol = tols(0)
For i As Integer = 1 To tolCount - 1
If tols(i) < bestTol Then
bestTol = tols(i)
End If
Next
body.GetExistingFacets(bestTol, vertCount, facetCount, vertCoords, normVectors, vertInds)
Else
' Calculate a new mesh based on the input tolerance.
body.CalculateFacets(Tolerance, vertCount, facetCount, vertCoords, normVectors, vertInds)
End If
Dim tg As TransientGeometry = body.Application.TransientGeometry
' Calculate the range of the mesh.
Dim smallPnt As Point = tg.CreatePoint(vertCoords(0), vertCoords(1), vertCoords(2))
Dim largePnt As Point = tg.CreatePoint(vertCoords(0), vertCoords(1), vertCoords(2))
For i As Integer = 1 To vertCount - 1
Dim vertX As Double = vertCoords(i * 3)
Dim vertY As Double = vertCoords(i * 3 + 1)
Dim vertZ As Double = vertCoords(i * 3 + 2)
If vertX < smallPnt.X Then
smallPnt.X = vertX
End If
If vertY < smallPnt.Y Then
smallPnt.Y = vertY
End If
If vertZ < smallPnt.Z Then
smallPnt.Z = vertZ
End If
If vertX > largePnt.X Then
largePnt.X = vertX
End If
If vertY > largePnt.Y Then
largePnt.Y = vertY
End If
If vertZ > largePnt.Z Then
largePnt.Z = vertZ
End If
Next
' Create and return a Box as the result.
Dim newBox As Box = tg.CreateBox()
newBox.MinPoint = smallPnt
newBox.MaxPoint = largePnt
Return newBox
Catch ex As Exception
Return Nothing
End Try
End Function
-Brian