Geometric diff for mechanical CAD: structured, rendered, CI-gateable diffs of STEP files. git diff for atoms.
Project description
argus-diff
Site & waitlist: argusdiff.com · Live demo: the Action commenting a geometric diff on a real PR
git diff for atoms. Geometric diff for mechanical CAD: point it at two revisions of a CAD file — STEP, STL, 3MF, OBJ, PLY — and it tells you what actually changed — bodies added/removed/modified, mass and volume deltas, bounding box growth, solid-solid interference — as structured JSON, a terminal summary, and a rendered before/after/overlay image. With CI gate flags, so a mechanical change can fail a pull request the way a broken test does.
AI agents now produce engineering artifacts faster than humans can review them. Verification is the bottleneck. ECAD has diff/review tooling; mechanical CAD — the larger population — reviews screenshots in Slack. This fixes that.
Install
# PyPI release is queued; until then install from GitHub:
pip install "argus-diff[render] @ git+https://github.com/mikelmyers/argus-diff"
Use
argus diff old.step new.step
argus diff old.step new.step --json diff.json --render diff.png --density 2.70
argus diff old.step new.step --fail-on-interference --max-mass-delta-pct 2 # CI gate
Example: a sensor-mount bracket between two revisions — plate thickened, mounting holes opened up, boss enlarged and shifted, a gusset added (modeled slightly overlapping the boss — caught), a locating pin deleted:
argus-diff 0.1.0
before: bracket_rev_a.step (4 bodies)
after: bracket_rev_b.step (4 bodies)
+ 1 added - 1 removed ~ 2 modified = 1 unchanged
volume: 35638.9 -> 48318.3 mm^3 (+35.58%)
mass: 96.23 -> 130.46 g @ 2.7 g/cm^3 (+34.23 g)
interferences (after): 1
~ body_0 -> body_0: vol +32.46%, com shift 1.000 mm
. 4x cylinder face: radius 2.500 -> 3.000 mm, area 94.25 -> 150.80 mm^2, moved 1.000 mm
. 2x plane face: area 468.00 -> 624.00 mm^2, moved 1.000 mm
~ body_1 -> body_1: vol +52.38%, com shift 6.325 mm
. cylinder face: radius 10.000 -> 12.000 mm, area 879.65 -> 1055.58 mm^2, moved 6.325 mm
+ body_2: 540.0 mm^3
- body_2: 100.5 mm^3
! interference body_1 x body_2: 108.650 mm^3 overlap
Yes — that's the four corner holes opening from ⌀5 to ⌀6 and the boss growing from ⌀20 to ⌀24, localized to the faces, by name. (Face output truncated here; run it to see all lines.)
Exit codes: 0 ok, 1 a gate failed, 2 load/usage error — drop it straight
into CI.
In CI / on pull requests
argus ci diffs every STEP file changed between two git refs and emits a
PR-comment-ready markdown report plus per-file renders:
argus ci --base origin/main --render-dir renders/ --markdown report.md \
--fail-on-interference --max-mass-delta-pct 2
Or use the GitHub Action — it comments the report on the PR and uploads the renders as artifacts:
# .github/workflows/cad-review.yml
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
cad:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mikelmyers/argus-diff@main
with:
density: "2.70"
fail-on-interference: "true"
max-mass-delta-pct: "2"
Read more
What changed in this 70 MB STEP file? Now you can actually know. — argus-diff run on two real revisions from the Jubilee toolchanger's history.
How it works
- STEP is loaded with the open-source OCCT kernel (via cadquery/OCP); assemblies are flattened to solids.
- Each body gets a geometric fingerprint: volume, surface area, center of mass, bounding box, volume-normalized principal moments of inertia, face and edge counts.
- Bodies are matched between revisions greedily by fingerprint distance; matched-but-different = modified, unmatched = added/removed.
- Interference = pairwise boolean-common volume between solids in the new revision (bbox-prefiltered).
- Renders are tessellated and drawn offscreen with pyvista — works headless
under
xvfb-run.
Every number in this README is reproducible: python examples/make_examples.py
generates the part pair, the command above generates the output.
Scope (honest)
Alpha. Today: STEP/STL/3MF/OBJ/PLY in, body-level diff out; change
localization is analytic on B-rep ("4x cylinder face: radius 2.500 ->
3.000 mm") and region-based on meshes ("changed region near (149, 124, 2)
mm: max deviation 2.02 mm", rigid translation removed first and stated).
--cache makes repeat runs on unchanged files near-instant (measured
480x on a 2x70 MB pair). Mesh files skip the interference check (no exact
booleans on meshes yet) and report it as skipped, not as zero; open
(non-watertight) meshes report volume as unavailable rather than a
made-up number. Not yet: rotation-aware mesh alignment (ICP),
feature-tree awareness, drawing/PMI diff, proprietary formats (SolidWorks
et al.). Roadmap is driven by what users actually hit — file an issue.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file argus_diff-0.1.1.tar.gz.
File metadata
- Download URL: argus_diff-0.1.1.tar.gz
- Upload date:
- Size: 475.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a0955258ad3e585466b4b1ad37947420f2fcd66a2aee47c6faa0c0a12976ec7
|
|
| MD5 |
5f888c6fdd488ee092c663d9ff8e92d0
|
|
| BLAKE2b-256 |
3ae7d1b5e4ff48d332e65d8c79f85bce48518dac7f26e6c27c6a687cdf038a52
|
Provenance
The following attestation bundles were made for argus_diff-0.1.1.tar.gz:
Publisher:
release.yml on mikelmyers/argus-diff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
argus_diff-0.1.1.tar.gz -
Subject digest:
5a0955258ad3e585466b4b1ad37947420f2fcd66a2aee47c6faa0c0a12976ec7 - Sigstore transparency entry: 1789981186
- Sigstore integration time:
-
Permalink:
mikelmyers/argus-diff@fe649a2a73a99a6fa105b29227dafd45f168a8ff -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mikelmyers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fe649a2a73a99a6fa105b29227dafd45f168a8ff -
Trigger Event:
push
-
Statement type:
File details
Details for the file argus_diff-0.1.1-py3-none-any.whl.
File metadata
- Download URL: argus_diff-0.1.1-py3-none-any.whl
- Upload date:
- Size: 23.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6281dbbcaec435c2b67f5cc1e4c67c93cc10300907b2d655e029ceabccfe76ad
|
|
| MD5 |
f70e3e2e77af0d51c6b1311ae49be3ae
|
|
| BLAKE2b-256 |
0f9690223241bea0167ec3f4584ed803c5f8afbee376dabefafb8ae3f536ce0d
|
Provenance
The following attestation bundles were made for argus_diff-0.1.1-py3-none-any.whl:
Publisher:
release.yml on mikelmyers/argus-diff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
argus_diff-0.1.1-py3-none-any.whl -
Subject digest:
6281dbbcaec435c2b67f5cc1e4c67c93cc10300907b2d655e029ceabccfe76ad - Sigstore transparency entry: 1789981250
- Sigstore integration time:
-
Permalink:
mikelmyers/argus-diff@fe649a2a73a99a6fa105b29227dafd45f168a8ff -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mikelmyers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fe649a2a73a99a6fa105b29227dafd45f168a8ff -
Trigger Event:
push
-
Statement type: