Documentation
CAM machining kernel
From a manufacturing job to NC and evidence: planning, cutters, voxel stock with a remaining-volume bracket, and certified collision checks.
What the CAM kernel is
The CAM kernel in packages/core/src/cam/kernel (20 source files, about 5 260 lines) takes a revisioned manufacturing job to machine NC plus evidence about that NC. It chooses cutters and cutting data, plans operations from the CAD features, runs the strategies, posts G-code, replays the NC independently against volumetric stock, and checks the motion of the tool assembly for collisions. Its algorithm identity is cadcamfun-cam-kernel@1.
| Module | Role |
|---|---|
| materials | workpiece grades with their machinability data |
| cutters | tool assemblies and the default library (70 cutters) |
| cutting-data | speeds, feeds, depths, load and deflection for a cutter in a material |
| planner | CAD features → ordered operations with their cutters |
| execute | prepare / run: strategies, post, independent NC replay, stock lineage, claims and release level |
| kinematics, head | trunnion table (AC / BC) and tilting head (B about Y) |
| assembly | the 3D tool assembly (cutting zone, shank, holder) as a profile and its solid of revolution |
| collision | continuous check of a joint move: certified clear, collision or unresolved |
| motion | simultaneous five-axis: locations → table poses, each move split and checked |
| stock | volumetric stock as voxels with a remaining-volume bracket |
| machine-clearance | holder and shank against the table over every NC move |
| convex | exact distance between convex hulls (GJK) |
export const prepareJob: (job: ManufacturingJob) => CamResult<PreparedJob>
export const executeJob: (prepared: PreparedJob) => CamResult<CamArtifact>
export const runJob: (job: ManufacturingJob) => CamResult<CamArtifact> // prepare, then execute
export const planOnly: (job: ManufacturingJob) => CamResult<{ operations; notes; unmachined }>
export const KERNEL_ALGORITHM = "cadcamfun-cam-kernel@1" Feature-based planning
The planner sequences a prismatic part the way a CAM programmer does, from the features the parametric history really has:
- face the stock top
- rough around the outlines and inside the pockets (adaptive or contour, with a wall allowance)
- counterbores, spot drilling, drilling or helical boring, countersinks, thread milling
- finish the walls
- break the top edges with a 2D chamfer when deburring is asked
- 3D roughing and finishing of freeform bodies
A body without a feature history (a mesh, a Boolean of primitives, a sculpted part) is machined with the 3D surface strategies. Planning chooses tools and regions only; cutting data is computed per operation when the job runs. A real run on a 44 × 44 × 11 mm POM-C blank planned three operations: face with facemill-50, surface-rough with flat-16 and surface-finish with ball-10, released as verified-program with a 149 s cycle time. That run is drawn in native manufacturing.
Cutters and the tool assembly
The default library has 70 cutters of nine kinds: flat (Ø1–16, plus aluminium ZrN variants), ball (Ø1–10), bull nose, face mill, spot drill, chamfer mill, engraver, thread mills (P0.5–P2) and drills. A cutter's assembly is its half-section profile from the tip, split into cutting zone, shank and holder.
{"id":"flat-6","name":"End mill Ø6 3F carbide ALTIN","kind":"flat","diameter":6,"flutes":3,
"material":"carbide","coating":"altin","fluteLength":15,"shankDiameter":6,"stickout":29,
"holder":{"diameter":40,"length":50}}
{"points":[{"r":0,"z":0},{"r":3,"z":0},{"r":3,"z":15},{"r":3,"z":29},{"r":20,"z":29},{"r":20,"z":79},{"r":0,"z":79}],
"parts":["cutting","cutting","shank","holder","holder","holder"],"length":79,"assumedHolder":false} Voxel stock and the remaining-volume bracket
Stock is a voxel grid. After a set of moves each voxel is material, removed sure (its whole cube lay inside the cutter at some sampled position) or removed possible (its cube came within the cutter's expansion: half a diagonal plus half the sample spacing). The remaining volume is therefore a bracket: material not possibly removed is definitely left, material not surely removed may be left. A voxel is never called removed without a sampled cutter covering it. Tilted tools carry their axis per tip point, so five-axis sweeps use the same two tests.
export const voxelStock: (min: Vec3, max: Vec3, voxel: number) => VoxelStock // sample step = voxel / 2
export const removeTipPath: (stock: VoxelStock, cutter: Cutter, path: ReadonlyArray<Vec3>, axes?: ReadonlyArray<Vec3>) => void
export const remainingVolume: (stock: VoxelStock) => { lower: number; upper: number } const s = voxelStock({ x: 0, y: 0, z: -10 }, { x: 40, y: 30, z: 0 }, 0.5)
// 80 × 60 × 20 = 96 000 voxels, step 0.25
removeTipPath(s, flat6, path) // slot y = 15, x 8→32, then the ring 8..32 × 8..22, all at z −3
remainingVolume(s) // {"lower":9749.5,"upper":10740} 124 ms
// stock 12 000 mm³: between 1 260 and 2 250.5 mm³ removed The bracket narrows with the voxel size: halving the voxel halves the slack band along every wall.
Continuous collision checking
A controller moves its joints linearly between two points, and a clash can happen between them. checkMotion checks the whole move: probes (the holder and shank, each non-cutting profile segment enclosed by a capsule) against obstacles (the table envelope and the part box). The lower bound is a Lipschitz argument: a signed distance changes by at most the displacement of the moving solid, so the move is bisected until every interval is proven clear by margin, an overlap is sampled, or the depth (12) runs out.
| Verdict | Meaning |
|---|---|
| clear | on every interval the lower bound of the clearance is at least margin |
| collision | a sampled probe point lies inside an obstacle; at says where along the move (0 to 1) |
| unresolved | an interval could not be proven clear within the depth, and no overlap was found |
// rapid along X at nose z 60
{"verdict":"clear","clearance":26.875,"evaluations":2}
// plunge from nose z 60 to nose z 0
{"verdict":"collision","clearance":-10,"at":1,"evaluations":2}
// standing poses
z 60 {"verdict":"clear","clearance":46.875,"evaluations":2}
z 20 {"verdict":"clear","clearance":6.875,"evaluations":2}
z 10 {"verdict":"unresolved","clearance":-3.125,"evaluations":4097}
z 5 {"verdict":"collision","clearance":-5,"at":0,"evaluations":1} The rapid reports 26.875 rather than the standing 46.875 because a moving check certifies a lower bound: it subtracts the displacement over each interval it did not subdivide further. The nose-z-10 pose shows why unresolved exists: the certified lower bound is negative (the envelope might overlap) yet no sampled point is inside, so after 4 097 evaluations the checker reports what it can prove instead of guessing. Envelopes are conservative: a capsule encloses its holder segment, so an overlap can be reported where the real tool would only come close.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| job status failed: Unsupported job schema version | job built for a different kernel schema | rebuild the job with the current schemaVersion |
| remaining-volume bracket wide | voxel too coarse for the features | use a smaller voxel; the slack is about one voxel along each wall |
| collision on a rapid | holder or shank envelope reaches the table or part | raise the clearance plane or shorten the stickout |
| unresolved on a long rotary move | the Lipschitz bound is weak far from the rotation centre | split the move, or accept the margin the checker could prove |
| operation planned with an unexpected cutter | cutter suitability for the material and rigidity | pass a tool library with the cutter you want |