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.