Skip to content
CADCAMFUN
Docs

Documentation

CAM and G-code

Generate toolpaths, post machine programs, replay emitted NC, and inspect the evidence attached to each result.

From geometry to a program

@cadcamfun/core exposes manufacturing functionality under cam. Operation generators turn geometry and machining parameters into toolpath moves. Posts turn a machine-neutral program into controller text. The generator adapter also supports mill, lathe, and 3D-printer requests used by the application form.

The native manufacturing kernel adds an ordered pipeline: prepare the part and resources, plan or accept operations, compute cutting data, generate motion, emit one NC program, replay that NC independently, and attach evidence. Generated text, a parsed program, and a verified manufacturing artifact are different outputs with different guarantees.

Emit an existing program
import { cam } from "@cadcamfun/core"

const post = cam.getPost("fanuc")
if (post === undefined) throw new Error("Post is unavailable")
const nc = post.emit(program, { decimals: 3, tolerance: 0.01 })

Toolpath representation

A program contains ordered toolpaths, each associated with a tool identifier and move list. Coordinates are millimetres, cutting feeds are millimetres per minute, and durations are seconds. Cutting depth is negative Z relative to stock top in conventional operation coordinates. Posts convert output units without changing the domain convention.

Moves are Rapid, Linear, Arc, Dwell, Comment, Cycle, and Orient. Arc centers are absolute XY coordinates; a post computes controller-relative I/J offsets. Matching start and end XY coordinates denote a full circle. A cycle retains drill, dwell, peck, or chip-break intent with its retract plane and feed.

Rotary fields on linear and rapid moves express simultaneous axis motion; Orient expresses indexed positioning. These angular fields use degrees. A dialect that cannot support rotary motion rejects such a program instead of discarding its rotary fields.

Operations and the generator API

The operation library includes profile, pocket, drilling, facing, engraving, surface machining, rest machining, threading, chamfering, adaptive machining, and multiaxis generation. Each operation exposes its parameter contract and pure generation function. Tool dimensions, depth limits, clearance, feeds, and geometry must satisfy the operation's validation rules.

POST /api/toolpaths/generate accepts generator settings, geometry, and printer extras and returns gcode, cycles, warnings, stats, program, and normally generation evidence. A valid request that cannot generate a program returns 400 with the reason; an unexpected computation failure becomes 500. Generation does not save a toolpath.

/api/toolpaths/suggest derives element-driven parameters, feedback, cutting statistics, operation text, and warnings. /api/toolpaths/parse interprets supplied G-code into program moves and statistics. Suggestions and parsing do not establish that a program is safe or geometrically correct.

Controller dialects

The built-in registry implements generic, fanuc, grbl, and haas. linuxcnc is a recognized post identifier without an implementation in this registry; getPost can return undefined. Resolve and check the post before calling it.

Posts share an emitter with fixed-decimal formatting, comment sanitization, tool changes, arc handling, optional line numbering, and cycle expansion. Defaults are three decimal places, canned cycles enabled, IJK arcs, header enabled, and 0.01 mm tolerance. Unsupported arc formats fall back through the dialect's supported forms and linearization.

toolNumbers maps tool identifiers to explicit positive integer pockets. Unmapped tools use first-use numbering while avoiding occupied numbers. retractBeforeToolChange is opt-in; it raises Z to the program's highest reached position before a tool change. Subprogram emission is also opt-in and depends on dialect support. Review the selected controller's output and machine configuration before production use.

Repeated toolpaths can be emitted as subprograms. A toolpath qualifies when, after its positioning move, its motion is another toolpath's motion translated within 5 × 10⁻⁶ mm, with the same move kinds, feeds, and arc senses. Each group needs at least six body moves and must save at least twelve moves in total. Toolpaths with canned cycles or rotary words are never candidates (packages/core/src/cam/post/subprograms.ts).

Native manufacturing and release levels

POST /api/toolpaths/manufacture accepts CamKernelRequest, a version 1 CAD snapshot with identity, material, optional machine, stock, tools, operations, quality controls, and work offset. Missing operations invoke the planner; missing tools use the default cutter crib. Unsupported input, unknown material or tool, empty part geometry, invalid stock, and unplannable work return explicit issues.

The kernel computes per-operation feeds, spindle speed, engagement, power and force estimates, warnings, time, and removed volume. It posts one program and independently interprets its emitted NC. The artifact retains the program, cutters, NC text, stock statistics, operation outcomes, identity digests, claims, and issues.

release is blocked, preview, or verified-program. Hard failures or empty NC block release. Verification requires the relevant claims to pass; the current release rule permits unsupported machine-travel evidence while still marking the program verified. Inspect each claim, including unsupported and unresolved statuses, before deciding whether the evidence covers the intended machine.

Claims cover cutting data, machine limits, completed operations, rapid clearance, holder clearance, gouging, material removal, NC replay, machine travel, and machine clearance. Evidence is tied to the artifact's NC and source identity. Editing NC or changing a source setup invalidates that connection unless the job is evaluated again.

Replay verification and its limits

POST /api/toolpaths/verify replays either G-code or a program against axis-aligned stock. Supply exactly one motion source, a tool, and stock bounds. Optional fixtures are axis-aligned envelopes; optional target solids or meshes enable gouge and remaining-stock comparisons. Missing flute, shank, stickout, or holder dimensions leave the corresponding checks unmodelled.

The verifier samples motion into a height field and returns issues, removal statistics, engagement/air-cut lengths, and a display preview. The default tolerance is 0.05 mm. Resolution defaults from cutter diameter and may be coarsened to fit the compute budget; ResolutionCoarsened reports that change. Grid resolution limits the geometric evidence the result can support.

A result with ok: true has no error-severity issue. Without a target it cannot establish finished-part accuracy; without fixtures or holder geometry it cannot establish their clearance. The kernel's integrated replay and machine checks add evidence specific to its captured job, while standalone verification reflects only its supplied request.

Implementation map

cam/ir.ts owns moves and program contracts; operations/ owns strategy generators; post/ owns controller emission; generator/ adapts application settings and parses text; model/ builds part geometry; verify/ owns height-field replay; kernel/ owns manufacturing orchestration and evidence. setup.ts and indexing.ts supply work-frame validation and indexed table transforms.

Saving documents, checking drawing access, and retaining source evidence belong to sidecar services. See Setups for portable operation documents and persistence checks.

Depths, heights, feeds, and defaults

Core operation parameters distinguish positive cut depth from absolute Z planes. totalDepth and stepdown are required positive finite lengths; a 5 mm depth with 2 mm stepdown produces passes at 2, 4, and 5 mm below the top. Defaults apply only when a field is missing, so an explicitly allowed zero is retained.

FieldDefault / unitMeaning
clearanceHeight10 mmPlane used for clear rapid positioning.
safeHeight5 mmOperation feed entry/exit height.
retractHeight2 mmParking height between passes; zero can be valid.
feed / plungeFeed800 / 300 mm/minPositive cutting and plunge feeds.
spindleRpm / coolant10,000 / offOperation-level spindle and coolant intent.

Heights must satisfy clearance ≥ safe ≥ retract. Profile defaults are outside compensation, climb direction, zero finishing allowance, direct lead-in, arc fitting off, and 0.01 mm tolerance. Ramp angle defaults to 3° and accepts 0.1–45°; helix pitch defaults to 1 mm per revolution. A declared side and allowance move the tool-center path, rather than changing the source contour.

These are pure operation defaults. The application generator's settings and native kernel's material-derived process data are separate parameter layers; do not assume the same field names or defaults across them.

Source: packages/core/src/cam/params.ts and operations/profile.ts.

Thread milling uses the ISO 68-1 basic profile heights: 0.54127 × pitch for internal threads and 0.61343 × pitch for external threads (packages/core/src/cam/operations/thread.ts). The toolpath viewer assumes 500 mm/min for cutting moves without a feed and 1,000 mm/min for rapids when timing an animation; these are playback assumptions, not machine estimates.

Replay a small NC program

This in-memory example cuts a 4 mm-wide straight slot 3 mm deep through a 40 × 40 × 10 mm stock model. It exercises the same core verifier used by the HTTP endpoint without saving a toolpath.

Inspect stock removal and collisions
import { cam } from "@cadcamfun/core"

const report = cam.verify.verifyToolpath({
  gcode: "G21\nG90\nG0 X-10 Y0 Z5\nG0 Z0\nG1 Z-3 F200\nG1 X10 F400\nG0 Z5\nM30",
  stock: { minX: -20, maxX: 20, minY: -20, maxY: 20, minZ: -10, maxZ: 0 },
  tool: { kind: "endmill", diameter: 4 },
  fixtures: [], tolerance: 0.05, resolution: 0.25
})
console.log(report.ok) // true for this stock/tool model
console.log(report.stats.removedVolume)
console.log(report.issues)

Changing the plunge to G0 Z-3 produces a RapidInMaterial finding. Adding a short cutting length or holder dimensions can introduce shank/holder findings that the original request could not test. Supplying target geometry adds finished-part comparison; this example makes no claim about a target or fixtures.

The verifier budgets at most 2,500,000 grid cells and 3,000,000 path samples, with an 80,000,000 work budget. Requests can be coarsened to meet those limits. Preview stock is downsampled for display, so its pixels are not a second, finer verification result.

Source: packages/core/src/cam/verify/verify.ts and verify/verify.test.ts.

When replaying text that calls subprograms, the interpreter follows M98 nesting up to eight levels deep and stops unrolling at 2,000,000 lines, so a large repeat count cannot exhaust memory (packages/core/src/cam/subprogram-text.ts).

Read a failed or preview artifact

A kernel issue includes a tag, severity, stage, message, and optional operation ID, NC line, and point. Follow that stage to the input that caused it: preparation checks stock/material/tools; planning selects strategies; cutting-data checks process limits; generation creates motion; posting writes the controller format; replay/evidence checks the resulting bytes.

  • NoSuitableTool or UnknownTool: inspect the selected cutter ID and geometry limits, including diameter, flute length, stickout, and the supplied cutter library.
  • RapidInStock or HolderCollision: locate the reported operation/point and inspect entry, retracts, stock, and holder/table clearance. Increasing cutting feed does not repair an unsafe rapid.
  • Gouge: check contour side, top/bottom levels, finishing allowance, and target geometry. The kernel tests an inside profile of the outside part as a real replay failure.
  • RemainingStock: inspect inaccessible corners, tool radius, finish strategy, and verification resolution before interpreting the remaining volume.
  • ReplayMismatch: treat posted motion and its claimed IR as inconsistent and regenerate from the captured job.

Machine presets represent typical classes, not measured machines. The available snapshot preset IDs are vmc-standard, haas-vf2, hsc-mill, desktop-mill, and cnc-router. The first is the default. Inspect actual machine travel and clearance evidence instead of inferring it from a preset's display name.

Source: packages/core/src/cam/kernel/execute.ts, machines.ts, and kernel.test.ts; wire issue fields are in packages/api/src/cam-kernel.ts.

Generate a pocket and post it

The CAM layer is a pipeline of pure functions: geometry and parameters go into an operation, the operation returns a Toolpath of intermediate-representation (IR) moves, a Program groups toolpaths, and a post processor turns the program into dialect-specific text. Nothing before the post knows about G-code words, so the same toolpath can be emitted for GRBL, Fanuc, Haas, or the generic dialect.

pocket.ts — complete, runnable with bun
import { cam } from "@cadcamfun/core"

const tool = { id: "t6", name: "6 mm flat end mill", kind: "endmill",
               diameter: 6, flutes: 3, maxRpm: 18000 }
// 60 × 40 × 10 mm block; its top face is at origin.z (Z = 0)
const stock = { x: 60, y: 40, z: 10, origin: { x: 0, y: 0, z: 0 },
                material: "aluminium-6061" }

const params = new cam.PocketParams({
  name: "Pocket 30x20",
  totalDepth: 3,   // mm below the stock top
  stepdown: 1.5,   // two levels: -1.5 and -3.0
  entry: "ramp",   // default is "helix"
})

const ring = [{ x: 15, y: 10 }, { x: 45, y: 10 }, { x: 45, y: 30 }, { x: 15, y: 30 }]
const pocket = cam.generatePocket(params, ring, tool, stock)

const program = { name: "pocket-demo", units: "mm", workOffset: "G54",
                  toolpaths: [pocket] }
const gcode = cam.getPost("grbl")!.emit(program)
console.log(gcode)
Pocket toolpath: 30 × 20 mm, 3 mm deep, Ø6 end mill Drag to orbit · scroll to zoom · right-drag to pan
The moves generated by the example below, drawn in machine coordinates. Dashed amber lines are rapids (G0); green lines are feed moves (G1). Watch the ramp entry spiral down at the start of each 1.5 mm level, then the offset rings work outward (order insideOut). The translucent block is the 60 × 40 × 10 mm stock, whose top face is Z = 0.
Output (GRBL, 85 lines, middle elided)
(PROGRAM: pocket-demo)
(UNITS: MM)
(WORK OFFSET: G54)
(OP 1: Pocket 30x20 | T1 [t6] | S10000 | F300-800)
G21 G90 G17 G94
G54
(OPERATION: Pocket 30x20)
(TOOL T1)
M3 S10000
(Pocket: Pocket 30x20)
G0 X30.000 Y21.000 Z10.000
Z5.000
Z2.000
Z1.000
G1 X36.000 Z0.732 F300
Y19.000 Z0.643
X24.000 Z0.107
Y21.000 Z0.018
X30.000 Z-0.250
X36.000 Z-0.518
Y19.000 Z-0.607
X24.000 Z-1.143
Y21.000 Z-1.232
X30.000 Z-1.500
...
(the second level repeats the ramp from Z-1.500 to Z-3.000)
...
X18.000
Y27.000
G0 Z2.000
Z10.000
M5
M2

Reading the output from the top: the header comments list the program, units, work offset, and a per-operation summary (tool number, ID, spindle speed, and feed range). G21 G90 G17 G94 selects millimetres, absolute coordinates, the XY plane, and feed per minute. GRBL has no tool changer, so the first tool is announced as a comment and later tool changes become (CHANGE TO TOOL Tn) followed by M0. The pocket starts with rapids down through clearance (10), safe (5), and retract (2) heights, then ramps at the 300 mm/min plunge feed. Once the ramp reaches depth it switches to the 800 mm/min cutting feed. The program ends with M5 (spindle stop) and M2.

Empty geometry is reported, not silently skipped

If the region is not a closed boundary, the operation emits the comment (nothing to cut: no closed boundary) and no motion. Pass ring points as { x, y } objects. Search generated programs for that comment when an operation produces no cuts.

Operation parameters and defaults

Every operation's parameter class extends the same common fields. Heights are absolute Z values and must satisfy clearance ≥ safe ≥ retract; the schema rejects anything else. Depths are positive distances below the stock top; the last pass is clipped to totalDepth.

Common fieldDefaultMeaning
id, nameoperation, OperationIdentity shown in comments and summaries.
clearanceHeight10Height for moves between operations.
safeHeight5Height for moves between regions.
retractHeight2Height before plunging/feeding.
totalDepthrequiredPositive depth below the stock top.
stepdownrequiredPositive depth per pass.
feed800Cutting feed, mm/min.
plungeFeed300Feed for plunging and ramping, mm/min.
spindleRpm10000Spindle speed.
coolantoffCoolant mode.
OperationSpecific fields (defaults)
Pocketstepover 40 (% of diameter, 1–100), allowance 0, finishPass false, climb true, entry helix | ramp | plunge, order insideOut | outsideIn, helixPitch 1, helixRadius 0 (= tool radius), rampAngle 3° (0.1–45), tolerance 0.01, toolCentre false.
Profileside outside | inside | onLine, climb true, allowance 0, leadIn direct | ramp | helix | arc, helixPitch 1, helixRadius 0, leadRadius 2, rampAngle 3°, tabs [] (each tab: position 0–1 along the path, width, height), arcFit false, tolerance 0.01.
DrillNo stepdown or feed. peck 0 (one stroke), chipBreak false, dwell 0 s. Peck cycles keep 0.5 mm clearance above the last depth, and chip-break retracts are 0.5 mm.
Facestepover 60, overshoot 0 (= tool radius + 1 mm), alongX true, tolerance 0.01.
Engravetolerance 0.01.
ThreadThread milling; profile depth 0.54127 × pitch internal and 0.61343 × pitch external.
Adaptive, rest, surface, chamfer, multi-axisFurther strategies exported from the same module; chamfer adds tipOffset 0.2 and lead 1.

Construct parameters with new cam.PocketParams({ ... }) (or the matching class) so that defaults are applied and constraints are checked once. Each operation is also exported as a pair, such as cam.Pocket = { Params, generate }, for code that dispatches by operation kind.

Post processors and output options

Posts are registered by ID in cam.posts and looked up with cam.getPost(id). The linuxcnc ID is reserved but not implemented, so getPost("linuxcnc") returns undefined. Check for that before calling emit.

PostCanned cyclesDwell unitRotary axesTool changeProgram frame
genericyessecondsyesTn M6none
grblno (expanded to moves)secondsrefusedcomment, then M0 pausenone
fanucyesmillisecondsyesTn M6 + G43 Hn% / O####, subprograms
haasyesmillisecondsyes, G234 TCPTn M6 + G43 Hn% / O####, subprograms

All four accept arcs as ijk or radius; linearize is always possible. A program whose moves carry rotary angles is rejected by GRBL rather than silently losing the angles.

emit optionDefaultEffect
decimals3Digits after the decimal point.
lineNumbersoff{ start, step } writes N-words, for example N10, N20.
useCannedCyclestrueEmit drilling cycles where the dialect has them.
arcModeijkijk, radius, or linearize.
headertrueWrite the descriptive comment header.
tolerance0.01Chord tolerance when arcs are linearized.
toolNumbersfirst-use orderMap of tool ID to carousel pocket. Missing entries keep first-use numbering without colliding with explicit pockets.
retractBeforeToolChangeoffRaise Z to the highest point reached before a tool change.
stampabsentHeader stamp. Leave it out for deterministic output.
subprogramsoffWrite repeated, translated toolpaths once as an incremental subprogram (Fanuc and Haas).
Same program for a Fanuc control with line numbers
const text = cam.getPost("fanuc")!.emit(program, { lineNumbers: { start: 10, step: 10 } })
// %
// O1000
// (PROGRAM: POCKET-DEMO)          comments are upper-cased
// ...
// N10 G21 G90 G17 G94 G40 G49 G80
// N20 G54
// N30 T1 M6
// N40 G43 H1
// N50 M3 S10000

Byte-identical by default

Options that change output (tool-change retract, subprograms, stamps) are off by default, so regenerating a stored request produces identical text. The cam/gcode workflow step relies on this to detect stale programs.

Before sending a program to a machine

  1. Confirm the stock: size, the origin of its minimum corner, and that its top face is at origin.z. Every depth is measured from that face.
  2. Check heights against fixtures. Clearance, safe, and retract heights are absolute Z values in the work offset.
  3. Choose the post for the actual controller and confirm it is implemented (getPost returned a post).
  4. Replay the program with verification and read every issue. Do not treat the absence of an error comment as proof.
  5. If the NC text was edited by hand, regenerate or re-verify. Evidence computed for the original text no longer applies.
  6. Dry-run on the machine above the part with feed override reduced, as you would for any new program.

Generated code is not a machine guarantee

The kernel and posts check geometry, limits, and dialect rules they know about. They do not know your fixtures, tool lengths, or machine condition. A verified program is evidence, not a guarantee of a safe cut.

Native kernel evidence claims

Besides the generators shown above, the native kernel returns an artifact with ten claims. Each is passed, failed, unsupported or unresolved, with a detail string. The release level summarises them: blocked, preview or verified-program.

ClaimExample statusDetail returned by the kernel
cutting-datapassedEvery operation runs at cutting data computed for Acetal POM-C (Delrin) and its cutter
machine-limitspassedSpindle speed, feed, power, torque and cutter deflection within limits
operations-completepassed3 operations generated; every planned feature has an operation
no-rapid-in-stockpassedNo rapid move touches the stock as replayed
holder-clearancepassedShank and holder stay clear of the stock (3-axis height-field replay)
no-gougepassedNo cut below the part beyond 0.030 mm (one cell of lateral slack)
material-removedpassedThe replayed stock matches the part within 0.110 mm
nc-replaypassedInterpreted 3035 NC motions independently of the post; extents agree within 2.0e-3 mm
machine-travelunsupportedThe machine has no travel limits configured
machine-clearancepassed3035 moves checked; the holder and shank stay at least 6.500 mm from the table

The rows are the real result for the POM blank in the native manufacturing guide: 3 operations, 3071 NC lines, release verified-program. The artifact's machine post can also be linuxcnc in the schema, but no LinuxCNC post is implemented, so presets do not use it.