Parts List grouped by Size - working code

Parts List grouped by Size - working code

info83PHN
Advocate Advocate
834 Views
3 Replies
Message 1 of 4

Parts List grouped by Size - working code

info83PHN
Advocate
Advocate

Took some brain scratching, and honestly can't say I understand every line of code, but I managed to get a script runnign to export a csv file with a list of all bodies and their sizes, body names, and which Component it's from.

 

PartsList.jpg

Here's the code if it's of any use to anyone - it's not all my own code - I butchered from multiple places to get to this.

 

 

import adsk.core, adsk.fusion, traceback
import csv
import tkinter as tk
from tkinter import filedialog
import time
import os
import os.path

def traverse(occurrences, bodies):
    # recursive method to get all bodies from components and sub-components
    for occ in occurrences:
        for bod in occ.bRepBodies:
            bodies.append(bod) 
        if occ.childOccurrences:
            traverse(occ.childOccurrences, bodies)
    return bodies
    
    
def run(context):
    global app, ui, product, design, rootComp, features, userParams
    ui = None
    try:
        uSaveThis = 1
        app = adsk.core.Application.get()
        ui  = app.userInterface
        product = adsk.core.Application.get().activeProduct
        design = adsk.fusion.Design.cast(product)
        parts = {}
        rootComp = design.rootComponent

        msg = ''
        # Set styles of file dialog.
        fileDlg = ui.createFileDialog()
        fileDlg.isMultiSelectEnabled = True
        fileDlg.title = 'Fusion Open File Dialog'
        fileDlg.filter = '*.*'
        # Show file save dialog
        fileDlg.title = 'Fusion Save File Dialog'
        dlgResult = fileDlg.showSave()
        if dlgResult == adsk.core.DialogResults.DialogOK:
            msg += '\nFile to Save: {}'.format(fileDlg.filename)
        else:
            return

        # Get all Components below root       
        occs = rootComp.occurrences
        
        # add all bodies from all components in the collection 
        bodies = []
        bodies = traverse(occs, bodies)        
        
        for body in bodies:
            dimensions = body.boundingBox.minPoint.vectorTo(body.boundingBox.maxPoint).asPoint()
            dimensions = tuple(sorted(dimensions.asArray()))
            if dimensions not in parts:
                parts[dimensions] = dict(quantity=0, names=set(), comp=set())
            parts[dimensions]['quantity'] += 1
            parts[dimensions]['names'].add(body.name)
            parts[dimensions]['comp'].add(body.parentComponent.name)

        fname = format(fileDlg.filename) 
        with open(fname, 'w', newline='') as output:
            writer = csv.writer(output)
            writer.writerow(('no', 'quantity', 'd1', 'd2', 'd3', 'bodies', 'component'))
            for i, (dimensions, part) in enumerate(sorted(parts.items())):
                dimensions = tuple(map(lambda d: product.unitsManager.formatInternalValue(d,design.unitsManager.defaultLengthUnits + '^1',False), dimensions))
                writer.writerow((i+1, part['quantity']) + dimensions + (';'.join(part['names']),) + (';'.join(part['comp']),))
        print('Parts list saved to file: {}\r'.format(fname))

        ui.messageBox('CSV File Created')

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes
Accepted solutions (1)
835 Views
3 Replies
Replies (3)
Message 2 of 4

info83PHN
Advocate
Advocate

OK. so there's 1 issue I can't solve yet.

There are some sizes that appear identical but are listed separate, so I am thinking that there's some weird fraction of a millimeter, like 12.0000000012 that's making it be different.

Anyone ? - in this code, is there a way to trim the dimensions to, say, 3 decimal places ?

 

            dimensions = body.boundingBox.minPoint.vectorTo(body.boundingBox.maxPoint).asPoint()
            dimensions = tuple(sorted(dimensions.asArray()))
            if dimensions not in parts:
                parts[dimensions] = dict(quantity=0, names=set(), comp=set())
0 Likes
Message 3 of 4

Jorge_Jaramillo
Collaborator
Collaborator
Accepted solution

Hi,

 

You just need to round() each dimension value.  Here it is how I did it:

...
        N_DECIMALS = 4 # number of decimals of the values in CM's (which is internal unit dimension in Fusion360)
...
            dimensions = tuple(round(f, N_DECIMALS) for f in sorted(dimensions.asArray()))
...

Just set N_DECIMALS as you prefer.

 

Regards,

Jorge Jaramillo

Software Engineer

 

Message 4 of 4

info83PHN
Advocate
Advocate

@Jorge_Jaramillo  Thank You

I would not have found that in a million years !!

 

Just an additional note - because the code uses the Boundingbox to get the size, it will only work if the bodies are aligned to the origin planes. Any bodies that are tilted or at an angle, won't report a correct size.

 

Here's my updated code if anyone wants to experiment :

 

import adsk.core, adsk.fusion, traceback
import csv
import tkinter as tk
from tkinter import filedialog
import time
import os
import os.path

def traverse(occurrences, bodies):
    # recursive method to get all bodies from components and sub-components
    for occ in occurrences:
        for bod in occ.bRepBodies:
            bodies.append(bod) 
        if occ.childOccurrences:
            traverse(occ.childOccurrences, bodies)
    return bodies
    
    
def run(context):
    global app, ui, product, design, rootComp, features, userParams
    ui = None
    try:
        uSaveThis = 1
        app = adsk.core.Application.get()
        ui  = app.userInterface
        product = adsk.core.Application.get().activeProduct
        design = adsk.fusion.Design.cast(product)
        parts = {}
        rootComp = design.rootComponent

        msg = ''
        # Set styles of file dialog.
        fileDlg = ui.createFileDialog()
        fileDlg.isMultiSelectEnabled = True
        fileDlg.title = 'Fusion Open File Dialog'
        fileDlg.filter = '*.*'
        # Show file save dialog
        fileDlg.title = 'Fusion Save File Dialog'
        dlgResult = fileDlg.showSave()
        if dlgResult == adsk.core.DialogResults.DialogOK:
            msg += '\nFile to Save: {}'.format(fileDlg.filename)
        else:
            return

        # Get all Components below root       
        occs = rootComp.occurrences
        
        # add all bodies from all components in the collection 
        bodies = []
        bodies = traverse(occs, bodies)        
        
        for body in bodies:
            dimensions = body.boundingBox.minPoint.vectorTo(body.boundingBox.maxPoint).asPoint()

            N_DECIMALS = 4 # number of decimals of the values in CM's (which is internal unit dimension in Fusion360)
            dimensions = tuple(round(f, N_DECIMALS) for f in sorted(dimensions.asArray()))
            
            if dimensions not in parts:
                parts[dimensions] = dict(quantity=0, names=set(), comp=set())
            parts[dimensions]['quantity'] += 1
            parts[dimensions]['names'].add(body.name)
            parts[dimensions]['comp'].add(body.parentComponent.name)

        fname = format(fileDlg.filename) 
        with open(fname, 'w', newline='') as output:
            writer = csv.writer(output)
            writer.writerow(('no', 'quantity', 'd1', 'd2', 'd3', 'bodies', 'component'))
            for i, (dimensions, part) in enumerate(sorted(parts.items())):
                dimensions = tuple(map(lambda d: product.unitsManager.formatInternalValue(d,design.unitsManager.defaultLengthUnits + '^1',False), dimensions))
                writer.writerow((i+1, part['quantity']) + dimensions + (';'.join(part['names']),) + (';'.join(part['comp']),))
        print('Parts list saved to file: {}\r'.format(fname))

        ui.messageBox('CSV File Created')

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

0 Likes