L1 · PAI-150

From geometry to toolpath

Generate a valid roughing + finishing toolpath for a pocket and explain how tool diameter and stock define what the path can and cannot reach.

01
Challenge

Try this first — before any explanation.

Select a tool and generate a toolpath (or paths) for the pocket — 30×20×8 mm with 4 mm internal corner radii — then Run Cut and clear it fully: flat floor, walls in place, corners matching the 4 mm radius. Pick a tool too big and the corners stay full; skip roughing and the middle never clears; pick ∅3 and it works but takes forever. Try to clear it on purpose.

The Bench

The full Bench is a WebGL voxel cutter; here it is a numpy voxel sim. Pick tools and stepover, then run the cut — the autograder checks cleared %, corners, gouge, entry, and time.

corners sharper than r are unreachable.\n from numpy import roll\n reach = region_mask.copy()\n rr = int(np.ceil(r_vox))\n yy, xx = np.mgrid[-rr:rr+1, -rr:rr+1]\n disk = (xx**2 + yy**2) <= r_vox**2\n # a voxel is reachable if a disk centered within region fits over it\n erob = region_mask.copy()\n for dy in range(-rr, rr+1):\n for dx in range(-rr, rr+1):\n if dx*dx+dy*dy <= r_vox*r_vox:\n erob &= np.roll(np.roll(region_mask, dy, 0), dx, 1)\n reachable = np.zeros_like(region_mask)\n for dy in range(-rr, rr+1):\n for dx in range(-rr, rr+1):\n if dx*dx+dy*dy <= r_vox*r_vox:\n reachable |= np.roll(np.roll(erob, dy, 0), dx, 1)\n removed |= (reachable & region_mask)\n return removed","label":"2 — Tool sweep (a round tool erases a disk)"},{"code":"np.random.seed(2101)\nrough_tool_d = 6.0 # EDIT — must be <= 8 mm to reach the 4 mm corner\nfinish_tool_d = 6.0 # EDIT — finishing tool for the corners\ndo_rough, do_finish = True, True\n\nremoved = np.zeros_like(NOMINAL)\nsim_time = 0.0\nif do_rough:\n removed = sweep(removed, rough_tool_d, NOMINAL)\n sim_time += NOMINAL.sum()*GRID*GRID / (rough_tool_d*2.0) # bigger tool = faster\nif do_finish:\n removed = sweep(removed, finish_tool_d, NOMINAL)\n sim_time += NOMINAL.sum()*GRID*GRID / (finish_tool_d*2.0)\n\ncleared_pct = 100.0 * (removed & NOMINAL).sum() / NOMINAL.sum()\nleftover = (NOMINAL & ~removed)\npar_time = NOMINAL.sum()*GRID*GRID / (6.0*2.0) * 2\nprint(f'cleared={cleared_pct:.1f}% leftover_voxels={int(leftover.sum())} sim_time={sim_time:.1f} par={par_time:.1f}')","label":"3 — Run your cut (EDIT the tools)"},{"code":"corner_ok = (NOMINAL & ~removed).sum()*GRID*GRID < 0.2 # leftover area tiny\nentry_ok = True # both tools fit the 30 mm pocket\ngouge = bool((removed & ~NOMINAL).any())\nproblems = []\nif cleared_pct < 99.5:\n if not do_rough:\n problems.append('POCKET_INTERIOR_NOT_CLEARED: a FINISH_CONTOUR only cuts the boundary - add a ROUGH_POCKET pass.')\n else:\n problems.append(f'cleared only {cleared_pct:.1f}%.')\nif not corner_ok:\n problems.append(f'CORNER_NOT_CLEARED: tool radius {finish_tool_d/2:.1f} mm > 4.0 mm corner - choose finishing tool diameter <= 8 mm.')\nif gouge:\n problems.append('GOUGE: stock removed outside the pocket - check stock_to_leave >= 0.')\nif sim_time > 1.8*par_time:\n problems.append(f'SLOW: sim_time {sim_time:.0f} = {sim_time/par_time:.1f}x par - rough with a larger tool, finish with the small one.')\n\nif cleared_pct >= 99.5 and corner_ok and not gouge and sim_time <= 1.8*par_time:\n print(f'PASS - cleared {cleared_pct:.1f}%, corners OK, no gouge, time {sim_time:.0f}<=1.8x par. '\n 'A round tool can never cut an internal corner sharper than its own RADIUS.')\nelse:\n print('FAIL - ' + ' '.join(problems[:2]))","label":"4 — Autograder (seed 2101)"}],"intro":"The full Bench is a WebGL voxel cutter; here it is a numpy voxel sim. Pick tools and stepover, then run the cut — the autograder checks cleared %, corners, gouge, entry, and time.","key":"manufacturing/from-geometry-to-toolpath","kind":"python","title":"From geometry to toolpath"}">
PYTHON · NUMPY · IN-BROWSER

From geometry to toolpath

The full Bench is a WebGL voxel cutter; here it is a numpy voxel sim. Pick tools and stepover, then run the cut — the autograder checks cleared %, corners, gouge, entry, and time.

02
Model

The idea, built visually.

A milling machine can't draw. It can only spin a round tool and drag it through metal — so the real question is what shapes a round tool can leave behind. Subtractive manufacturing is erasing: the empty region is the sweep of the tool's circle along its path.

Here's the limit — a tool can never cut a corner sharper than its own radius. Want a 4 mm internal corner? Your tool radius must be ≤ 4 mm (a ∅8 end-mill or smaller). So we split the job: roughing hogs out the bulk fast with a bigger tool leaving a little stock; finishing takes one light pass with a tool small enough for the corners. Fast and accurate.

▣ Stage animation: Top-down: a blue circle (the tool) sweeps the grey stock, erasing it; zoom on a 4 mm corner where a grown ∅12 circle leaves an orange un-removable crescent while a ∅6 kisses it clean.

03
Guided practice

Build it up, step by step.

  1. Step A (worked): ∅6 rough (3 mm radius ≤ 4 mm corner) with stock_to_leave=0.4, then ∅6 finish to nominal → cleared 100%, corners OK.
  2. Step B (fade): switch the rougher to ∅10 to reproduce the corner failure, then pick a corner-capable finisher to recover.
  3. Step C (independent): the tab_slot — 5 mm wide, smallest radius 2.5 mm — tools wider than 5 mm return ENTRY_BLOCKED; author a rough+finish plan from scratch.
04
Feedback

How the Bench grades your run.

PASS WHEN On the assigned feature, cleared_pct ≥ 99.5%, corner_radius_ok True, gouge False, entry_ok True, and sim_time ≤ 1.8× par on seed 2101.

  • CORNER_NOT_CLEARED: 4 leftover crescents, tool radius 5.0 mm > corner radius 4.0 mm. Choose a finishing tool with diameter ≤ 8 mm.
  • POCKET_INTERIOR_NOT_CLEARED: ~38% remaining at center. A FINISH_CONTOUR only cuts the boundary — add a ROUGH_POCKET pass.
  • ENTRY_BLOCKED: a ∅6 tool cannot enter a 5.0 mm-wide slot. Required tool diameter < 5.0 mm.
  • SLOW: sim_time = 4.1× par. A ∅3 tool clearing the whole bulk is correct but wasteful — rough with a larger tool, finish with the small one.
05
Retrieve & space

Bring back what you've already mastered.

  • From M1.1: why can no end mill produce a 0 mm (sharp) internal corner, and what is the cheapest fix? → it always leaves its own radius; accept a min-tool-radius fillet.
  • From M1.2: a 2.5 mm radius slot 6 mm deep is a 2.4:1 ratio — is that comfortable for milling or a hint to reconsider the process?
  • Forward link: predict what halving stepover does to cleared %, finish, and sim_time (sets up 2.2's triangle).
06
Mastery gate

What you must demonstrate to advance.

On the unseen tab_slot part, a rough+finish plan grades to cleared_pct ≥ 99.5, corner_radius_ok True, gouge False, entry_ok True, sim_time ≤ 1.8× par — and answer 'a tool can't cut a corner sharper than its own ____' (radius).

07
Project

How this feeds your build.

Where each machined feature gets its tool + toolpath; in M5 the factory runs many back-to-back, so a badly chosen tool here becomes a yield/finish defect there. Banks the bracket pocket toolpath + the tab_slot plan.