Announcements
Attention for Customers without Multi-Factor Authentication or Single Sign-On - OTP Verification rolls out April 2025. Read all about it here.

UNDO via API

MichaelT_123
Advisor

UNDO via API

MichaelT_123
Advisor
Advisor

Dear All In The Know,

 

of F360 On The Expert Level.

 

We all deal with residue issues every day. The common practice is to dispose of the previous day dross and start a day fresh and light. This is a somehow instinctive process, which generally does not require manual intervention.

In F360 we have UNDO facility which allows us to conduct a similar function on our design's excesses/misadventures, but to my knowledge only manually.

You probably noticed that the bulkiness/length of UNDO list has a significant effect on design regeneration time. This is particularly the case while working on a complex/big project or/and using API script-driven processes on it. At least to be less general in the statement, it is my experience. Nevertheless, I anticipate that many users encounter similar phenomena.  Some more fusionate F360 aficionados might even have their own benchmarking tools to measure it.

 

The question:

                   "Is there any API tool/function which would allow controlling/purging UNDO list?"

I do not have to mention that...

                 "The existence of such a process would be a great relief!"

do I?

 

With Regards

MichaelT

 

PS.

... without closing/re-opening a design.

 

MichaelT
0 Likes
Reply
1,454 Views
10 Replies
Replies (10)

goyals
Autodesk
Autodesk

There is no UNDO API available but there is a deleteMe API on objects which you can use to replicate the same behaviour but after that there is no way you can redo it.



Shyam Goyal
Sr. Software Dev. Manager
0 Likes

MichaelT_123
Advisor
Advisor

Hi Mr. Goyals,

 

Thank you for the replay and... sorry to hear that there is no relief in sight. I will try to hold on using all means available to my disposal. It is not a comfortable situation, but I hope that lax time will come.

 

As per your advice that deleteMe "can use to replicate the same behaviour", I allow myself to disagree.

Many operations, which are recorded as transactions onto UNDO list do not concern a deletable object. Some examples which come to mind are changes to appearances, modifications of parameters (both users and model/dimensions/formulas), modifications of features arguments values, timeline rearrangements, sketches modifications, and perhaps many more.

They are not (unless they are the last changes/modifications) constituencies of design in time moment and particularly they are not saved when a document is closed. Re-opening the same document will result in an empty UNDO list.

Nevertheless, per my observation UNDO list content/length/bulk has an effect on the design responsiveness/regeneration time as somehow the list is taking part in it. This negative effect can be quite pronounced, especially in a case of complex designs where multiple changes to it have been accumulated (into UNDO list) and particularly exaggerated by side scripting processes of different natures.

The detailed analysis of what happens is beyond me, as I observe the black box only, but even basic insight into the gastric tract of F360 by capable nurse/doctor should find the obstacle.

The remedy could be quite simple :

  • Limit the allowable depth of UNDO stack
  • Build in the UNDO flush button ( or two allowing for an ecological half flush)
  • Possibly expose it via API

On the nicer noteโ€ฆ let's imagineโ€ฆ changes have been madeโ€ฆ the update released andโ€ฆ

suddenly you start hearing the thunder of 24 deep relief's UFFFFFFFFsssss with amplitude increasing every hour and finally the 25th one, the loudest camming from SF.

 

With Wishes of SSSuCCCeSSS

MichaelT

MichaelT
0 Likes

kandennti
Mentor
Mentor

Hi MichaelT_123.

 

Try this.

import adsk.core, adsk.fusion, traceback
def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        cmd = ui.commandDefinitions.itemById('UndoCommand')
        cmd.execute()

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

I found a way to call Fusion360's native commands.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/api-for-working-with-selection-sets/m-p/88...

 

 

 

2 Likes

MichaelT_123
Advisor
Advisor

Hi Mr. Kandennti,

 

The title of the post is short and I agree it is not precise enough.

The code you presented restores design to a stage preceding the topmost transaction on UNDO stack.

The problem I have tried to describe is of a different nature. It is about How to remove the transaction entry from the UNDO stack without affecting a design itself.

I can understand that messing up with history-based design paradigm can be quite challenging and could potentially open the pandoraโ€™s box, unconstrained!. On the other side, it could also bring important benefits. The quite significant one would be the increase the F360 processes responsiveness as the size of UNDO stack (based on my experience and the recent evidence by Mr. JeromeBriot ( https://forums.autodesk.com/t5/fusion-360-api-and-scripts/time-increase-when-a-script-is-executed-su...)) slows down F360 in many cases quite considerably.

As far as I know, currently there is only one way to purge/collapse UNDO stack in a safe way. It is by invoking <CloseFile><OpenFile> command pair. It is effective as it clears UNDO stack and also restores F360 responsiveness, but in many scenarios is not a practical solution.

So there is the questionโ€ฆ for DEVELOPERS.

Would it be possible to wrap <CloseFile><OpenFile> command pair into <FlushMePleaseAndForgetWhatYouHaveDoneSoFar>

or potentially implement external parameter <FlushOrNotToFlush>?

 

Regards

MichaelT

MichaelT
2 Likes

kandennti
Mentor
Mentor

I found TextCommands to delete Undo history.

Fusion.TrimHistoryStream

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/blob/master/TextCommands/TextCo... 

 

Here is a sample code that I actually tested.

#Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import time
import tracemalloc

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)

def run(context):
    try:
        useTrimHistory = True # <- Please change
        stepTrimHistory = 10 # <- Please change
        bodyCount = 100 # <- Please change

        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface
        _app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        des  :adsk.fusion.Design = _app.activeProduct
        root :adsk.fusion.Component = des.rootComponent

        tracemalloc.start()
        t = time.time()
        dumpMsg(f'start - {log_memory()}')
        
        adsk.doEvents()

        bodies = initBoxes(bodyCount)

        baseFeatures = root.features.baseFeatures
        baseFeature = baseFeatures.add()
        rootBodies = root.bRepBodies
        for body in bodies:
            baseFeature.startEdit()
            rootBodies.add(body, baseFeature)
            baseFeature.finishEdit()
            dumpMsg(f'body count:{rootBodies.count}-{log_memory()}')
            adsk.doEvents()
            if (not rootBodies.count % stepTrimHistory) and useTrimHistory:
                _app.executeTextCommand(u'Fusion.TrimHistoryStream')

        dumpMsg(f'Done _ time:{time.time() - t}')
    except:
        _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def initBoxes(
    count) -> list:

    unitBox = 1

    pnt3D = adsk.core.Point3D
    vec3D = adsk.core.Vector3D
    pnt = pnt3D.create(0.0, 0.0, 0.0)
    lVec = vec3D.create(1.0, 0.0, 0.0)
    wVec= vec3D.create(0.0, 1.0, 0.0)

    bouBox3D = adsk.core.OrientedBoundingBox3D
    box = bouBox3D.create(pnt, lVec, wVec, unitBox, unitBox, unitBox)

    tmpBrMgr = adsk.fusion.TemporaryBRepManager.get()
    bodies = [tmpBrMgr.createBox(box) for idx in range(count)]

    return bodies

def dumpMsg(msg :str):
    global _ui
    _ui.palettes.itemById('TextCommands').writeText(str(msg))

def format_bytes(size):
	power = 2 ** 10  # 2**10 = 1024
	n = 0
	power_labels = ['B', 'KB', 'MB', 'GB', 'TB']
	while size > power and n <= len(power_labels):
		size /= power
		n += 1
	return 'used memory: {:.3f} {}'.format(size, power_labels[n])
 
def log_memory():
	snapshot = tracemalloc.take_snapshot()
	size = sum([stat.size for stat in snapshot.statistics('filename')])
	return format_bytes(size)

 

It seems to have a slight effect on processing speed.
However, there were times when the created Bodies were not displayed, though not every time.
I could not find a way to make them appear except to save the file and reopen it, which gave me an unstable impression.

 

3 Likes

MichaelT_123
Advisor
Advisor

You are The Legend Kandenneti-san,

 

I haven't tested the code in more complex scenario yet, however I will let you know the result if I have performed it.

 

Best Regards & HNY

MichaelT

MichaelT
0 Likes

kandennti
Mentor
Mentor

The reason why the body was hidden was that the state of the eye mark in the GUI and the isLightBulbOn property in the API were not the same.

1.png

By setting isLightBulbOn=True in the API, we were able to restore the correct state.

 

Also, it seems that "Transaction.Trim" performs the same processing as "Fusion.TrimHistoryStream".

2 Likes

MichaelT_123
Advisor
Advisor

Hi Kandennti-san,

It could be some transactional bug...

I hope that TF360 take the notice and check it.

Regards

MichaelT

MichaelT
0 Likes

MichaelT_123
Advisor
Advisor

Hi Kandennti-san,

 

I have conducted some tests, but by any means, they were not intensive ones. Resultsโ€ฆ?

I would describe it them as mixed. The text call function trimHistoryStream() undoubtedly performs UNDO-stack trimming.

It removes/deletes many entries there, but not all. I have noticed that for example 'Undo edit parameter' and

'Undo Drive Joints' stay on the stack. Perhaps there are some other non-trimmable items also.

The bodies created during the scope of trimHistoryStream() operation become invisible !!!. I have not to succeed lightBulb on them using API call. Scarry think is that the bodies made outside the scope of trimHistoryStream() become infected also !!!. They can be cured by saving and opening the file though.

The reliability of the function is the open question. Perhaps this is some try&error attempt to address the underlaying issue.

 

I would prefer more 'common sense' approach:

  • establish a user-modifiable list of actions/transaction types to be pushed onto UNDO stack
  • place a circular buffer at the entry of transactions into UNDO stack
  • allow a user to determine the size of the buffer
  • filter transactions entering the buffer via the list in point 1
  • in the background purge the bottom of the stack by reasonable chunks
  • and it is done!

For a person like you, I am absolutely sure that it would need only a break between two sake shots to code it!

 

Best Regards

MichaelT

MichaelT
0 Likes

kandennti
Mentor
Mentor

@MichaelT_123 

 

I know almost nothing about Transaction.

 

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/blob/master/TextCommands/TextCo... 

This list is old, but the latest version seems to have a few more commands for Transaction.xxx.

 

There may also be something available for TestTransaction.xxx.

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/blob/master/TextCommands/TextCo... 

 

You can actually type this in TextCommands(Txt) and check it out.

Transactionใ€€/?

TestTransaction /?

 

If you want to see all the commands, try this one.

TextCommands.List /hidden

 

0 Likes