Metadata-Version: 2.4
Name: gcode-collision-check
Version: 0.1.0
Summary: Offline collision detection for CNC tool assemblies against a static scene, based on G-code.
Project-URL: Homepage, https://github.com/LukasMoravansky/gcode-collision-check
Project-URL: Repository, https://github.com/LukasMoravansky/gcode-collision-check
Project-URL: Issues, https://github.com/LukasMoravansky/gcode-collision-check/issues
Author-email: Lukáš Moravanský <luk.moravansky@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cam,cnc,collision-detection,fcl,gcode,manufacturing,milling,trimesh
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Requires-Dist: click
Requires-Dist: numpy
Requires-Dist: python-fcl
Requires-Dist: trimesh[easy]
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# gcode-collision-check

**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.

<table>
<tr>
<td width="50%"><img src="docs/images/no_collision_1.png" alt="Safe toolpath: green tool assembly clears the vise"></td>
<td width="50%"><img src="docs/images/collision_1.png" alt="Collision: red tool assembly interfering with the vise"></td>
</tr>
<tr>
<td align="center"><b>safe</b> — the whole assembly clears the vise and stock</td>
<td align="center"><b>collision</b> — flagged with G-code line, XYZ, and penetration depth</td>
</tr>
</table>

## Quick start

```
gcode-collision-check verify program.nc --scene vise.stl
```

See [examples/](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).

## What it checks

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`).



## Supported G-code

Fanuc-style dialect (covers Fanuc, Haas, Mazak, and most generic
postprocessors):

- `G0`, `G1` — rapid / linear feed
- `G2`, `G3` — circular interpolation (I/J/K center format only, no R-format)
- `G17`/`G18`/`G19` — arc plane selection
- `G20`/`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 compensation
- `G54`–`G59` — work coordinate systems
- `G73`, `G80`–`G89` — canned cycles (expanded into rapid/feed moves;
retract always targets the R-plane, G98/G99 not distinguished)
- `G90`/`G91` — absolute / incremental
- `M3`/`M4`/`M5` — spindle (parsed, ignored)
- `M6 Tn` — tool change
- `M30`/`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).

## Tool configuration

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).

### Program origin (part zero / datum)

`--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`.

### Program rotation (part turned in the vise)

`--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).

## Visualizing a result

```
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.

<p align="center">
  <img src="docs/images/collision_2.png" alt="Close-up of the tool assembly interfering with the vise jaw, toolpath traced in yellow" width="80%">
</p>

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 into `PATH` instead 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.

## Limitations

- 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



## How it works

```
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)
```



## Background

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/](docs/research/) for the full collision library landscape
analysis.

## License

MIT