Offline collision checker for CNC G-code. Verifies the full tool assembly (endmill + holder + collet) against your vise, stock, and fixtures — before the program runs on the machine.
No open-source tool does this. CAMotics and FreeCAD Path simulate material removal but don't check if your holder will crash into the vise jaw. This one does.
![]() |
![]() |
| safe — the whole assembly clears the vise and stock | collision — flagged with G-code line, XYZ, and penetration depth |
gcode-collision-check verify program.nc --scene vise.stl
See examples/ for a runnable crash/safe pair and the actual
CLI output. To see the result in an interactive 3D view instead of text,
use gcode-collision-check visualize (see below).
The tool path is sampled into thousands of XYZ positions along the program. At each position, the entire tool assembly — cutter, shank, and holder — is placed at that position and checked for overlap against the static scene (vise, stock, fixtures, table). Most real-world crashes are the holder or collet nut hitting a vise jaw, not the cutting edge itself — a check that only looks at the cutter tip misses exactly those.
gcode-collision-check verify crash.nc --scene vise.stl
RESULT: COLLISION 33 hits on 3 of 7 program lines
checked 7 segments, 134 samples, 0.07 s
LINE G-CODE COLLIDING PAIR HITS DEPTH mm Z
---- -------------------------------- -------------------------- ---- -------- -------------
6 G0 X-50 (rapid into ... flute / vise 8 40.087 5.00
7 G1 Z-10 F200 flute / vise, shank / vise 9 9.798 -10.00..5.00
8 G0 Z50 flute / vise, shank / vise 16 10.535 -10.00..20.00
first contact: line 6 X-36.00 Y0.00 Z5.00
One G-code move is sampled into many positions, so a single line can produce
many collision samples. The report groups them by program line: HITS is how
many sampled positions collided on that line, DEPTH mm is the deepest
penetration found there (contact means a zero-depth touch, not a real dig-in),
and Z is the height — a single value, or a lo..hi range when it varies
across the line. The full per-sample list is preserved in the JSON report
(--output report.json).
Fanuc-style dialect (covers Fanuc, Haas, Mazak, and most generic postprocessors):
G0,G1— rapid / linear feedG2,G3— circular interpolation (I/J/K center format only, no R-format)G17/G18/G19— arc plane selectionG20/G21— inch / mm (all internal units are mm)G28— home (simplified to a plain rapid to the given coordinates)G40/G41/G42— cutter compensation (parsed, not applied)G43 Hn/G49— tool length compensationG54–G59— work coordinate systemsG73,G80–G89— canned cycles (expanded into rapid/feed moves; retract always targets the R-plane, G98/G99 not distinguished)G90/G91— absolute / incrementalM3/M4/M5— spindle (parsed, ignored)M6 Tn— tool changeM30/M2— program end(...)and;...comments,N-numbers,%program delimiters
Not supported: macro variables (#100...), subprograms (M98/M99,
O-calls), Siemens/Heidenhain conversational syntax, parametric
programming, 4th/5th axis (A/B/C).
Either build a tool from individual dimensions:
gcode-collision-check verify program.nc --scene vise.stl \
--tool-diameter 10 --tool-flute-length 25 \
--tool-shank-diameter 10 --tool-shank-length 30 \
--tool-holder-diameter 46 --tool-holder-length 50 \
--tool-kind flat
--tool-kind is flat, ball, or bull (bull requires
--tool-corner-radius > 0).
Or use a built-in preset, which overrides the individual --tool-* flags:
gcode-collision-check verify program.nc --scene vise.stl --tool-preset 6mm_ball
Available presets: 6mm_ball, 10mm_flat, 12mm_bull.
Other options: --wcs-offset X,Y,Z (G54 offset from machine home),
--output report.json (write the full result as JSON), --quiet
(suppress the stdout summary).
--origin X,Y,Z sets a single machine-frame point that the program's
(0, 0, 0) maps to, applied to every move regardless of which WCS (G54–
G59) is active in the G-code:
gcode-collision-check verify program.nc --scene vise.stl --origin 150,75,0
This replaces the per-WCS offset logic entirely for the whole program — it's
for the common case where the part datum is known once (e.g. from a probing
cycle) and every WCS in the file should be measured from it. --origin and
--wcs-offset are mutually exclusive; passing both is an error. If neither
is given, behavior is unchanged: each WCS defaults to (0, 0, 0) unless
overridden with --wcs-offset.
--origin-rotation A,B,C rotates the whole program, in degrees, around its
own origin — the datum set by --origin, or the active WCS offset if
--origin isn't given. It follows CSN ISO 841: A/B/C rotate around
X/Y/Z, positive is the right-hand rule around the positive half-axis
(e.g. C=90 sends the program's +X to machine +Y). Angles compose
extrinsically in the machine frame, X first, then Y, then Z
(R = Rz(C) @ Ry(B) @ Rx(A)).
gcode-collision-check verify program.nc --scene vise.stl --origin-rotation 0,0,30
Use it for a part that's clamped at an angle in the vise. It's independent of
--origin/--wcs-offset and can be combined with either — the rotation is
applied around whichever datum is in effect:
gcode-collision-check verify program.nc --scene vise.stl \
--origin 150,75,0 --origin-rotation 0,0,30
If --origin-rotation isn't given, behavior is unchanged (no rotation).
gcode-collision-check visualize program.nc --scene vise.stl
Runs the same check as verify, then opens an interactive 3D view in your
browser: the scene, the tool assembly placed at the first collision (or at
the end of the toolpath if it's safe), and the full toolpath traced in
yellow. The tool is colored red on collision, green when safe.
Above: a close-up of the interactive view at the collision point — the tool body up against the vise, with the drilling toolpath traced in yellow.
It takes the same --tool-*/--tool-preset/--wcs-offset/--origin/
--origin-rotation options as verify, plus:
--output-dir PATH— save the GLB/HTML intoPATHinstead of a temp directory (useful if you want to keep or share the files)--no-open— generate the files without launching a browser
The view is a self-contained scene.glb + scene.html (using
<model-viewer> from a CDN) — open scene.html directly if the browser
didn't launch automatically. Safari blocks file:// GLB loading via CORS;
use Chrome or Firefox if the view stays blank.
- Point sampling, not continuous collision detection (thin obstacles smaller than the sampling step may be missed)
- 3-axis only (no A/B/C rotary axes yet)
- Fanuc-style G-code only (no Siemens/Heidenhain conversational)
- Tool is modeled as cylinders (+ hemisphere/torus for ball/bull), not actual flute geometry
- No material removal — checks against the static stock shape, not what's actually left after each pass
G-code text
→ parse (RS-274 tokenizer + modal state) → GCodeSegment[]
→ sample (linear step / arc chord tolerance) → (position, line_no)[]
→ Z-prefilter (skip segments above every obstacle)
→ collision query (tool CollisionManager vs. obstacle CollisionManager)
→ VerifyResult (safe / CollisionEvent[] with line, position, depth)
Built with AI-assisted coding (Claude Code) based on original research into CNC collision detection across Python, C++, C#, NVIDIA Omniverse, and Unity stacks. The architecture, library selection, and validation were done against real machining programs at JIC Smart Factory. See docs/research/ for the full collision library landscape analysis.
MIT


