Fixing Horizontal Milling Toolpaths...


Horizontal milling in PixelCNC has always been a bit fudgy. For many projects it gets the job done but for some projects it's less than ideal in the toolpaths that it generates. The generation itself has come a long ways, and yet there's still a bit further we need to go before the horizontal toolpaths can be put to rest. In spite of all the progress made on the horizontal milling toolpath generation there's still currently one problem that needs attending to.

The issue I'd like to talk about has been haunting me since horizontal toolpaths were first up-and-running, and involves how cut paths around islands are ordered in combination with pocketing cut paths. Here's an example of some islands, including some islands that also contain pockets - which also contain more islands. The hierarchy of islands and pockets can get pretty complex!


Islands Galore!

This is actually a relatively simple case. Many *real* projects can be far more complicated, in terms of their configuration of pockets and islands.

Horizontal cut paths generate off the sides of pockets and generate off the sides of islands in increments that are set at the operation's step-over from one-another. Pocket-emanating cut paths are clockwise (for conventional milling) while island cut paths are counter-clockwise. Watching a horizontal milling toolpath generate you can see how cut path 'wave fronts' emanate from the surfaces of the project at each successive cut depth:


Wave Fronts!

Generating a horizontal milling toolpath involves gathering up all of these generated cut paths and figuring out in which order the tool should follow them. The order in which the cut paths generate is not the order in which you want a tool to follow them, that would crash a machine in most cases. So, what PixelCNC does is detect whenever there are pockets which are shared by sets of cut paths, and order the cut paths from inner-most to outer-most, top-to-bottom, one pocket at a time.

Here you can see how cut paths are generated per cut depth, from the outer-most cut to the inner-most cut, but are cut in an order that's orthogonal to the order they were generated, cutting from the inside to the outside one pocket at a time:



The situation gets even more complicated when you have hierarchies of pockets, where one pocket's cut paths have deeper pocketing cuts, and/or multiple 'child' pockets - which may have child pockets of their own:



Notice how once a pocket's first layer of cuts are performed the tool moves on to the deeper child pockets' cut paths. Only once it has finished everything inside the initial pocket will it then move on to another pocket. In this example it then moves to an isolated area of the L-shaped pocket, on the top right, which it starts cutting the center of as its own pocket but then moves to the larger body of the pocket at the bottom and the cuts naturally envelop the started pocket on the top right. Once the whole first layer of that pocket is done it moves to the next depth - a depth it already cut at on the top left pocket! This ordering of cuts optimizes the operation run time by minimizing redundant retracted tool travel distance, such as if the first layer of cuts was performed across all pockets and *then* moving on to the next cut depth, one at a time, before moving to the last cut depth to cut the deepest pockets last. That would involve the tool visiting each pocket as many times as there are cut depths.

Islands are treated like inside-out pockets, but instead as neighboring/sibling pockets that should be cut separately, they are treated like sub-pockets, in terms of ordering their cut paths in combination with pocket toolpaths. This is where the problem arises, because most projects are not going to be nice-and-neat and clean like these example animations.

The problem manifests itself when an island is cut prematurely, because of outer cuts that haven't been traversed yet, but which should've. This results in the tool dragging through the material at full-engagement, like a slotting operation, which is not what you want when you have your spindle speed and feed rate set specifically for your cut step over size. You want each and every cut to only remove material at the cut step over size.

This is what PixelCNC will do at times, which is not good for a cutter or CNC if you have your feed rate, cut depth, and spindle speed all maxed out:


It's not the worst thing in the world, but it could be better. The material gets removed, right? Sure, but what would be better is if that outer 3-lobed bit of material around the 3 islands were removed before clearing out the material immediately surrounding the 3 individual islands, like this:



This maintains a much more consistent tool engagement with the material. There will be much less spiking in the amount of material that's touching the flutes of the tool at any one time. This is what should *always* happen, but there's not a very easy way to go about achieving this result consistently, at least  in terms of the existing pages of code that are responsible for generating horizontal toolpaths in PixelCNC. A whole re-write of the horizontal milling operation's toolpath generation algorithm could solve it, but I've grown to aim for what's pragmatic over what feels-good. I've re-written too much code too many times, starting a project over from scratch because I didn't like one specific situation, which ultimately is a big waste of time and energy because it ceases all forward progress.

The bright idea I had the other day was to just completely remove islands altogether, by laying down 'bridges' between islands and the edges of the pocket that contain them:



This works, but it requires extra computation finding when and where bridges should be placed, and results in somewhat longer toolpath generation times. There are a couple ideas I have for mitigating this, such as making better use of multi-core CPUs and doing more work in parallel, which could end up making this new variant of the horizontal toolpath generation faster than ever before. This would be nice, as horizontal milling toolpaths have always been the slowest to generate, taking a longer amount of time than just about all the other operations' toolpaths.

So far so good, but before this bridging strategy will fly I still need to have it detect if there are any other islands that are in the way of a bridge, which will require furthermore computational prep-work. Alternatively, I could just connect up islands to the nearest island or pocket edge, and just make sure there's always at least one island of a network of islands that is connected to the pocket they all lie inside of, to prevent turning all of them into a single island. I'm thinking this would be much faster than searching for a bridge to the outer pocket, because there'd be cases where an island is in the middle of a bunch of other islands and there simply is no way for it to connect up with the pocket that it's floating in.

At the end of the day, there's really dozens of ways to go about generating horizontal milling paths, and these are just my own adventures with generating offset-machining style pocketing toolpaths. The goal is simply to remove material from within 2D areas, at a fixed depth, and there's a number of ways to move a tool through material to accomplish that end result. Whatever gets the job done, does not take too long to generate, and involves the least amount of PixelCNC development time is what I'm aiming for.

Also: A new pocketing operation is in the works (which brought about all this horizontal milling hubbub, as they rely on similar code) which will allow using the 2D contours of an image to create pockets at arbitrary depths - regardless of the actual project Z depth or project image's depth within the pocket area. This will be used for generating toolpaths for inlays as well. I'm also working on an adaptive toolpath generation, which allows subsequent operations to only generate toolpaths where there's actually material left from previous operations, to eliminate cutting where material may have already been removed.

Thanks for reading!

Leave a comment

Log in with itch.io to leave a comment.