How to improve speed and memory of stl export and isLightBulbOn

How to improve speed and memory of stl export and isLightBulbOn

cadop5R9X9
Enthusiast Enthusiast
1,043 Views
9 Replies
Message 1 of 10

How to improve speed and memory of stl export and isLightBulbOn

cadop5R9X9
Enthusiast
Enthusiast

I use a script to generate stl files that iterates through components and turns the lightbulb on/off, then saves the scene.  

 

For a large model this is _extremely_ slow and causes problems with memory.  

 

Memory case:

Exporting all bodies in the scene is roughly 100mb.  Iterating in a script that turns off the bodies, and individually turns lightbulb on, then exports, will take 60gb of RAM. 

 

Speed case:

Turning the lightbulb of a _single_ body off or on can take 3 minutes. On a file with 30 components and 200 bodies, this process (turn lightbulbon->export stl) takes over 15 hours.

 

Generally the code structure is:

 

exporter = design.exportManager
for oc in components:
    oc.isLightBulbOn = False

for oc in components:
    oc.isLightBulbOn = True
    stl_options = exporter.createSTLExportOptions(root, file_name)
    stl_options.sendToPrintUtility = False
    stl_options.isBinaryFormat = True
    stl_options.meshRefinement = accuracy
    exporter.execute(stl_options)
   

    oc.isLightBulbOn = False

 

How can the memory be reduced and speed of lightbulb modifications changed?

0 Likes
Accepted solutions (1)
1,044 Views
9 Replies
Replies (9)
Message 2 of 10

Jorge_Jaramillo
Collaborator
Collaborator

Hi @cadop5R9X9 ,

 

I believe you can save a lot of time if on line #7 of your code, you just include the component you need to be exported on every iteration, instead of the root component when Fusion360 have to go over the whole model to see what is visible to be exported.

This way you don't need to turn on/off every component, which consumes also some time to be performed.

 

A second suggestion is to yield some time to Fusion360 between exports, so it can empty the events stack it might have.  You can do it by calling adsk.doEvents() after every file export.

 

Hope this help.

 

Regards,
Jorge

 

0 Likes
Message 3 of 10

cadop5R9X9
Enthusiast
Enthusiast
Hi Jorge, Thanks for the fast response.

Unfortunately the stl exporter does not preserve the document coordinates, so unless autodesk fixed that, this is the only way I know how to do it.

I will try the doEvents() though! I noticed there is a problem where Bodies are not being toggled to invisible, so maybe this also will fix that.
0 Likes
Message 4 of 10

cadop5R9X9
Enthusiast
Enthusiast

Unfortunately doEvents doesn't fix it, but I think it does show the bug in the exporter.  I can actually see in the GUI the state of the design, and can see that the bodies are hidden, but the exporter ignores the lightbulb attribute of the bodies and exports them all.  As a note, it works correctly at the component level, but fails on individual bodies (in both cases though it's very slow and doesn't release memory)

0 Likes
Message 5 of 10

Jorge_Jaramillo
Collaborator
Collaborator

Hi @cadop5R9X9 ,

 

Another option it to run the export in a custom event. I'd suggest to make a single export per event process.  Once you finish every export you post a new custom event, and then the event process is called back again.  You need a global variable to track the component index you are currently at.

 

Here is an example on how to implement a custom event, and also in a second thread.  I had found it handy in some cases.

 

Maybe it won't be faster, but I thing it will be safer in the way the user interface will be up-to-date.

 

Regards,
Jorge

0 Likes
Message 6 of 10

cadop5R9X9
Enthusiast
Enthusiast

Thanks, I'll try that tomorrow.  I don't think it will fix the body-visibility export bug, but it seems like there is a chance the secondary thread will help with memory if it gets shut down every time. 

0 Likes
Message 7 of 10

BrianEkins
Mentor
Mentor

The time it's taking you to process seems very excessive. There's something strange going on. Here's an entirely different approach that I would probably use. I don't have a huge assembly to test it on, but it was very fast for my simple test case.

 

import adsk.core, adsk.fusion, adsk.cam, traceback

_app = adsk.core.Application.get()
_ui  = _app.userInterface

def run(context):
    try:
        des: adsk.fusion.Design = _app.activeProduct


        newDoc: adsk.core.Document = _app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType, True) 
        newDes: adsk.fusion.Design = newDoc.products.itemByProductType('DesignProductType')
        newRoot = newDes.rootComponent
        exportMgr = newDes.exportManager
        exportAssembly(des.rootComponent.occurrences, newRoot, exportMgr)

        newDoc.close(False)
        _ui.messageBox('Finished.')
    except:
        _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))



def exportAssembly(occs: adsk.fusion.Occurrences, newRoot: adsk.fusion.Component, exportMgr: adsk.fusion.ExportManager):
    try:      
        occ: adsk.fusion.Occurrence = None
        for occ in occs:
            body: adsk.fusion.BRepBody
            for body in occ.bRepBodies:
                tBrep = adsk.fusion.TemporaryBRepManager.get()
                tBody = tBrep.copy(body)
                bf = newRoot.features.baseFeatures.add()
                bf.startEdit()
                newRoot.bRepBodies.add(tBody, bf)
                bf.finishEdit()

                newBody = newRoot.bRepBodies[0]
                occName = occ.name.replace(':', '_')
                bodyName = body.name.replace(':', '_')
                stlOptions = exportMgr.createSTLExportOptions(newBody, f'C:/Temp/STLExport/{occName}_{bodyName}.stl')
                exportMgr.execute(stlOptions)

                bf.deleteMe()

            exportAssembly(occ.childOccurrences, newRoot, exportMgr)
    except:
        _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 8 of 10

cadop5R9X9
Enthusiast
Enthusiast
Thank you! This does export way faster without the memory problems. I have to check if it covers the general issue of bodies not including their new coordinates/moved position.

But, how could I modify this to export occurences as a single body (includes its sub-bodies)? It seems to rely on `tBrep = adsk.fusion.TemporaryBRepManager.get()`, which seems to be a brep and not occurence compatible?

Also you are awesome, I was searching for the docs on this, and you were the first link 😄 https://ekinssolutions.com/using-temporary-b-rep-in-fusion-360/

0 Likes
Message 9 of 10

BrianEkins
Mentor
Mentor
Accepted solution

I'm not sure at what level you want to combine the bodies into a single STL. Here's a slight modification of the previous program where it exports each component that contains any bodies as an STL file. They'll still be positioned as they exist in the top-level assembly.

 

import adsk.core, adsk.fusion, adsk.cam, traceback

_app = adsk.core.Application.get()
_ui  = _app.userInterface

def run(context):
    try:
        des: adsk.fusion.Design = _app.activeProduct


        newDoc: adsk.core.Document = _app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType, True) 
        newDes: adsk.fusion.Design = newDoc.products.itemByProductType('DesignProductType')
        newRoot = newDes.rootComponent
        exportMgr = newDes.exportManager
        exportAssembly(des.rootComponent.occurrences, newRoot, exportMgr)

        newDoc.close(False)
        _ui.messageBox('Finished.')
    except:
        _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))



def exportAssembly(occs: adsk.fusion.Occurrences, newRoot: adsk.fusion.Component, exportMgr: adsk.fusion.ExportManager):
    try:      
        tBrep = adsk.fusion.TemporaryBRepManager.get()
        occ: adsk.fusion.Occurrence = None
        for occ in occs:
            combinedBody: adsk.fusion.BRepBody = None
            for body in occ.bRepBodies:
                tBody = tBrep.copy(body)

                if combinedBody is None:
                    combinedBody = tBody
                else:
                    tBrep.booleanOperation(combinedBody, tBody, adsk.fusion.BooleanTypes.UnionBooleanType)                    

            if combinedBody:
                bf = newRoot.features.baseFeatures.add()
                bf.startEdit()
                newRoot.bRepBodies.add(combinedBody, bf)
                bf.finishEdit()

                newBody = newRoot.bRepBodies[0]
                occName = occ.name.replace(':', '_')
                bodyName = body.name.replace(':', '_')
                stlOptions = exportMgr.createSTLExportOptions(newBody, f'C:/Temp/STLExport/{occName}.stl')
                exportMgr.execute(stlOptions)

                bf.deleteMe()

            exportAssembly(occ.childOccurrences, newRoot, exportMgr)
    except:
        _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

  

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 10 of 10

cadop5R9X9
Enthusiast
Enthusiast
okay so i was hoping there was an easier way, since I have nested components i need to flatten the list, but it is something i've done before so its feasible. Thanks so much!
0 Likes