GCode optimization for retracts and rapids

In the last few weeks, there’s been a bunch of discussion on this forum about how Carbide Create and some of the more advanced tools do not, or no longer, create optimal gcode for retracts and rapids movement, and there has been a request to build a post-processor tool to optimize the gcode files coming out of the CAM tooling.
I’ve started building such a tool, and this will be some sort of running thread of the progress in case folks want to help beta test this.

As always, if you have ideas or requests don’t hesitate to put in a comment

The tool can be accessed at


and source code is on github at https://github.com/fenrus75/FenrusCNCtools/tree/master/ncopt

Current supported optimizations:

  • Retracts can use G0 instead of just G1
  • Movement at the top are now converted to G0 rapids
  • Plunges are converted to rapids when possible
    (with more to come in the next while)

new optimization: Movement at the top height is now converted to rapids.

This one is slightly tricky since detecting what the top is… ends up harder than it sounds since there are some movements well above the retract height (like between toolpaths)


new optimization: “Rapid plunge”

if the gcode contain a G1 plunge at a location that the bit previously visited, use a G0 plunge to just above the previous location and then G1 the rest of the way

this should make Carbide Create cuts go much faster



Do you have a maintained description of the algorithm you’re using anywhere for people to view?

Also, are there any automated or manual tests?

I’m not that good at reading javascript sorry.

I’ll have to create the things you are asking for still… this stuff is like 3 hours old so far :wink:

I’m also quite new at javascript still… so I for sure am not using any fancy language features

I should go add a pile of documentation to the code next



I was asking if you had them already rather than asking you to create them if they didn’t though, not trying to make more work for you :wink:

Have you tried Sphinx for the docs?


I used Sphinx extensively on our Python tools and it is by far the easiest to maintain code docs tool I’ve seen yet, also https://readthedocs.org/ will host for free for open source projects so triggered builds from github can auto-export new code docs to readthedocs.

I can help out with Sphinx if that’s useful?


I’ve not used Sphinx before. I’m about to just add a pile of comments… once those are there I can either learn Sphinx or you can do a quick PR on github to sphinxiphy them :slight_smile:

comments put in for now, more later

next step is to write code to keep existing unmodified lines the same as much as possible to make doing diffs easier, this will be key for creating the test suite

1 Like

Looking good fenrus! I’ll review your code for comments in a minute.

I have my first draft typed up here:

It’s in python, and also about an hour old :stuck_out_tongue:

I have a plan for automatic tests, but have ran into an issue: In the original version of the routine, Fusion performed G0 plunges beneath the feed height. I don’t know how to detect that this is OK (I think that fusion is able to do so because they know that they already milled out that material).

Unfortunately, unless we can nail down how to replicate this behavior, we’ll have to manually validate everything.

I’ve manually inspected a diff of my result with the “good” source and found that it matches perfectly (minus a few comments Fusion adds, etc, as well as the plunge issue above and a minor bug which results in a few seconds of G1 at the start of each toolpath, so a tiny bit slower but nbd)

For my code, the relevant logic can be almost completely described by this snippet of code, which should be almost self-evident:

    conditionMovingDownPastClearance       = zAtEndOfLine[i] < feedHeightCurrent and zAtEndOfLine[i] < zAtStartOfLine[i]
    conditionMovingLaterallyBelowClearance = (zAtEndOfLine[i] < clearanceHeightCurrent or zAtEndOfLine[i] < zAtEndOfLine[i]) and (xAtStartOfLine[i]!=xAtEndOfLine[i] or yAtStartOfLine[i] != yAtEndOfLine[i] )
    bInsertG0BecauseVerticalUp=xAtStartOfLine[i]==xAtEndOfLine[i] and yAtStartOfLine[i]==yAtEndOfLine[i] and zAtEndOfLine[i]>=clearanceHeightCurrent and newGmode != 'G0' and lineGmodes[i]=='G1'
    bInsertG0BecauseLateralAbove=xAtStartOfLine[i]!=xAtEndOfLine[i] and yAtStartOfLine[i]!=yAtEndOfLine[i] and zAtEndOfLine[i]>=clearanceHeightCurrent and zAtStartOfLine[i]>=clearanceHeightCurrent and newGmode != 'G0' and lineGmodes[i]=='G1'
    bSwitchToG1=(conditionMovingDownPastClearance or conditionMovingLaterallyBelowClearance) and newGmode == 'G0' and lineGmodes[i] == 'G1'

    # if the current line does not move X or Y, and ends above the clearance height and is a G1 op, switch to G0
    # if the current line does move X and/or Y, but starts and ends above the clearance height, switch to G0

    if bInsertG0BecauseVerticalUp or bInsertG0BecauseLateralAbove:
        [newGmode, tokensLine] = addStartOfG0(tokensLine)

    # if the reassigned gMode (at the end of the upcoming line) is different from G1,
    # .... and the gMode at the start of the line was a G0
    # .... and the Z at the end of the line is less than the clearance height
    # .... set it to a G1

    elif  bSwitchToG1:
        [newGmode, tokensLine] = addStartOfG1(tokensLine, fAtEndOfLine[i])
        alreadyChangedModes = True

@fenrus thoughts on the plunge issue? It would be really nice to have an automated path to testing this in the event of future updates.

Want me to post the other 6 test projects (about 12 *.nc files)


OK @fenrus thoughts:

-I don’t write JS, but it appears as if you have hardcoded your feed height to be Z=0. If people set their zero to be the stock bottom instead of the stock top, I believe that your code will rapid straight through everything. Yikes! If you review my ugly python, I have a simple routine for pulling out the clearance/retract/feed height which are prominently placed at the beginning of each toolpath.
-Although your code is briefer, if you added spaces between your tokens and trimmed trailing zeros at the end of each floating point value, it would be easier to automatically diff your results
-Likewise, if you stripped redundant “G0” and “G1” commands, that would make using diff easier as well.

Great job! I like that you don’t have to install python to use it, lol


the top is zero shouldn’t be but I’ll re-audit the code for it

if you KNOW the gcode came from the current f360 then yes you can pull it out

I am trying to keep the tool much more generic though

as for the output, once my current cut is finished I’ll go back to coding and will fix the output diff issue, I have in my head figured out what it needs :wink:

Good points on all accounts. Hit me up when the diff thing is nailed!

I’m not positive on the zero thing, no JS experience and all

oh I appreciate the code review :wink:

the THEORY is that the code first computes a bounding box (min and max for each of X Y and Z) of the cuts, and then uses that bounding box as reasoning for all other things…

now I did borrow code from one of my other projects that DID assume zero-at-top so it could be something leaked in… so I will re-audit this

no problem!

One concern aside from the zero thing is that many movements happen at rapid speeds but below the bounding box of all toolpaths, because fusion likes to start things off by going to the retract height (typically 0.4" above retract, where rapids usually happen at). So you’ll miss out on some time savings

Is it perhaps an idea to have the tool first scan the supplied .nc file and then report a summary to the user of what it thinks it has found in the file, retract heights etc. and then ask the user to confirm or reject an “above here there be rapids” measure?

Might also be worth reporting how many traverses are identified as being convertable to rapid (or the sum of the travel distance which is ~ time)?

right now these are logged to the console
(in chrome: go to the three dots, then More Tools then Developer Tools)

1 Like

also I am pretty sure we can just detect what they are somehow…


So easy enough to output to the HTML later on once basics are working.

I was more thinking along the lines of getting the user to actually think about what the converter script was going to do.

There’ll be no shortage of “non technical” folk who want to use this and if they treat is as a magic black box then they are likely to suffer nasty surprises when they feed it some weird toolpath that breaks the rules and produces an unwanted output.

Doing a propose / confirm or propose / alter pair with the user to say “I think you want me to do this” is more likely to get them to think about what the tool is doing before just pressing “Run”. It might also give opportunity to do more aggressive optimisations if the user thinks that is safe, e.g. if they are going to run it through a sim before loading it to a machine.

Just thoughts on likely problems once rolled out.

yeah can do; I already have the click buttons in the UI, so the user can toggle on/off these optimizations conceptually…
I’ll figure out how to somehow show this in a way that’s not too confusing.

1 Like