gerberdiff 0.29.0__tar.gz → 0.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/ISSUE_TEMPLATE/config.yml +1 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/workflows/ci.yml +3 -3
- gerberdiff-0.30.0/.github/workflows/docs.yml +71 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/workflows/release.yml +2 -2
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/CHANGELOG.md +220 -16
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/CONTRIBUTING.md +11 -7
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/PKG-INFO +12 -9
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/README.md +4 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/SECURITY.md +5 -3
- gerberdiff-0.30.0/docs/changelog.md +1 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/docs/cli.md +17 -1
- gerberdiff-0.30.0/docs/contributing.md +1 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/docs/geometry-diff.md +7 -1
- gerberdiff-0.30.0/docs/index.md +78 -0
- gerberdiff-0.30.0/docs/javascripts/mathjax.js +20 -0
- gerberdiff-0.30.0/docs/reference/geometry.md +24 -0
- gerberdiff-0.30.0/docs/reference/package.md +32 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/docs/schema.md +17 -4
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/__init__.py +10 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/cli.py +59 -19
- gerberdiff-0.30.0/gerberdiff/diff/__init__.py +1 -0
- gerberdiff-0.30.0/gerberdiff/export/__init__.py +1 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/export/json_report.py +7 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/driver.py +8 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/expand.py +15 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/layer_geometry.py +50 -3
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/types.py +49 -0
- gerberdiff-0.30.0/gerberdiff/parse/__init__.py +1 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/gerber_state.py +27 -2
- gerberdiff-0.30.0/gerberdiff/render/__init__.py +1 -0
- gerberdiff-0.30.0/mkdocs.yml +87 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/pyproject.toml +17 -8
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_cli_diff.py +24 -1
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_cli_geomdiff.py +33 -4
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_cli_render.py +1 -1
- gerberdiff-0.30.0/tests/test_geometry_edge_cases.py +385 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_report.py +6 -2
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_gerber_state.py +68 -2
- gerberdiff-0.30.0/tests/test_indeterminate.py +178 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/uv.lock +552 -1
- gerberdiff-0.29.0/gerberdiff/export/__init__.py +0 -0
- gerberdiff-0.29.0/gerberdiff/parse/__init__.py +0 -0
- gerberdiff-0.29.0/gerberdiff/render/__init__.py +0 -0
- gerberdiff-0.29.0/tests/__init__.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.editorconfig +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.github/dependabot.yml +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.gitignore +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/.pre-commit-config.yaml +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/LICENSE +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/docs/api.md +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/docs/architecture.md +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/diff/diff_engine.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/diff/layer_matcher.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/export/png_export.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/export/svg_export.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/__init__.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/attribute.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/geom_diff.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/macro_geom.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/geometry/primitives.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/arc_math.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/excellon_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/gerber_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/macro_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/parse/tokenizer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/py.typed +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/render/compiled_render.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/render/draw_ops.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/render/macro_renderer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/render/renderer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/render/viewport.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/gerberdiff/types.py +0 -0
- {gerberdiff-0.29.0/gerberdiff/diff → gerberdiff-0.30.0/tests}/__init__.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/cairo_support.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/conftest.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/drill-inch-tz.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/drill-metric-lz.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-B.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-B.Mask.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-B.Paste.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-B.SilkS.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-Edge.Cuts.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-F.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-F.Mask.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-F.Paste.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-F.SilkS.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-In1.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-In2.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-In3.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-In4.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-NPTH.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-after/A64-OlinuXino-PTH.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-B.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-B.Mask.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-B.Paste.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-B.SilkS.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-Edge.Cuts.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-F.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-F.Mask.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-F.Paste.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-F.SilkS.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-In1.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-In2.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-In3.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-In4.Cu.gbr +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-NPTH.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/fixtures/gerbers-before/A64-OlinuXino-PTH.drl +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_arc_math.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_block_aperture.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_cli_parse.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_diff_engine.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_draw_ops.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_excellon_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_attribute.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_booldiff.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_expand.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_layer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_macro.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_oracle.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_geometry_primitives.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_gerber_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_json_report.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_layer_matcher.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_macro_parser.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_macro_renderer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_png_export.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_renderer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_scaffold.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_tokenizer.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_types.py +0 -0
- {gerberdiff-0.29.0 → gerberdiff-0.30.0}/tests/test_viewport.py +0 -0
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
name: Lint
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
18
|
- uses: astral-sh/setup-uv@v7
|
|
19
19
|
with:
|
|
20
20
|
enable-cache: true
|
|
@@ -32,7 +32,7 @@ jobs:
|
|
|
32
32
|
name: Type check
|
|
33
33
|
runs-on: ubuntu-latest
|
|
34
34
|
steps:
|
|
35
|
-
- uses: actions/checkout@
|
|
35
|
+
- uses: actions/checkout@v7
|
|
36
36
|
- uses: astral-sh/setup-uv@v7
|
|
37
37
|
with:
|
|
38
38
|
enable-cache: true
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
- python-version: "3.11"
|
|
52
52
|
os: windows-latest
|
|
53
53
|
steps:
|
|
54
|
-
- uses: actions/checkout@
|
|
54
|
+
- uses: actions/checkout@v7
|
|
55
55
|
- uses: astral-sh/setup-uv@v7
|
|
56
56
|
with:
|
|
57
57
|
enable-cache: true
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "gerberdiff/**"
|
|
10
|
+
- "CHANGELOG.md"
|
|
11
|
+
- "CONTRIBUTING.md"
|
|
12
|
+
- ".github/workflows/docs.yml"
|
|
13
|
+
# The site is built with `uv sync --group docs`, so what ships is decided
|
|
14
|
+
# by the lock, not just by the sources above. Without these two paths a
|
|
15
|
+
# dependency fix lands on main and the deployed site keeps serving the old
|
|
16
|
+
# build -- which is how mkdocs-material 9.7.6 stayed live on the published
|
|
17
|
+
# docs after GHSA-xvg9-69gf-fjrf was already patched in the lock.
|
|
18
|
+
- "uv.lock"
|
|
19
|
+
- "pyproject.toml"
|
|
20
|
+
pull_request:
|
|
21
|
+
branches: [main]
|
|
22
|
+
paths:
|
|
23
|
+
- "docs/**"
|
|
24
|
+
- "mkdocs.yml"
|
|
25
|
+
- "gerberdiff/**"
|
|
26
|
+
- "CHANGELOG.md"
|
|
27
|
+
- "CONTRIBUTING.md"
|
|
28
|
+
- ".github/workflows/docs.yml"
|
|
29
|
+
- "uv.lock"
|
|
30
|
+
- "pyproject.toml"
|
|
31
|
+
# Lets the site be rebuilt without inventing a commit -- there was previously
|
|
32
|
+
# no way to redeploy at all.
|
|
33
|
+
workflow_dispatch:
|
|
34
|
+
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
|
|
38
|
+
concurrency:
|
|
39
|
+
group: docs-${{ github.ref }}
|
|
40
|
+
cancel-in-progress: true
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
build:
|
|
44
|
+
name: Build site
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v7
|
|
48
|
+
- uses: astral-sh/setup-uv@v7
|
|
49
|
+
with:
|
|
50
|
+
enable-cache: true
|
|
51
|
+
- run: uv sync --group docs
|
|
52
|
+
- run: uv run mkdocs build --strict
|
|
53
|
+
- uses: actions/upload-pages-artifact@v5
|
|
54
|
+
if: github.event_name == 'push'
|
|
55
|
+
with:
|
|
56
|
+
path: site/
|
|
57
|
+
|
|
58
|
+
deploy:
|
|
59
|
+
name: Deploy to GitHub Pages
|
|
60
|
+
if: github.event_name == 'push'
|
|
61
|
+
needs: build
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
permissions:
|
|
64
|
+
pages: write
|
|
65
|
+
id-token: write
|
|
66
|
+
environment:
|
|
67
|
+
name: github-pages
|
|
68
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
69
|
+
steps:
|
|
70
|
+
- id: deployment
|
|
71
|
+
uses: actions/deploy-pages@v5
|
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
name: Build distribution
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
18
|
- uses: astral-sh/setup-uv@v7
|
|
19
19
|
with:
|
|
20
20
|
enable-cache: true
|
|
@@ -41,7 +41,7 @@ jobs:
|
|
|
41
41
|
needs: build
|
|
42
42
|
runs-on: ubuntu-latest
|
|
43
43
|
steps:
|
|
44
|
-
- uses: actions/checkout@
|
|
44
|
+
- uses: actions/checkout@v7
|
|
45
45
|
- uses: actions/download-artifact@v8
|
|
46
46
|
with:
|
|
47
47
|
name: dist
|
|
@@ -7,6 +7,91 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.30.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **BREAKING -- the exit-code contract now matches partspec and netspec**
|
|
15
|
+
([A3]). `0` no differences, `1` differences (with `--fail-on-diff`), `2` the
|
|
16
|
+
comparison could not be completed, `4` an input could not be read or parsed,
|
|
17
|
+
`64` usage (`EX_USAGE`). Previously `2` meant a parse error and `1` doubled as
|
|
18
|
+
both "differences found" and "could not write the output file". A gate written
|
|
19
|
+
`[ $? -eq 0 ]` is unaffected; one that treated `2` as a parse failure must now
|
|
20
|
+
read `4`.
|
|
21
|
+
- **Geometry report schema `version` 2 -> 3**, adding `summary.outcome`,
|
|
22
|
+
`summary.unrepresented` and a per-layer `unrepresented`. Additive: every v2
|
|
23
|
+
field keeps its meaning.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`geomdiff` has a third outcome, and says what it could not model** ([A3]) --
|
|
28
|
+
`identical` | `different` | `indeterminate`. A stroke drawn with a macro or
|
|
29
|
+
block aperture is not modelled by the geometry engine, and used to vanish: a
|
|
30
|
+
board with an added trace reported `0 changes` at exit `0`, with JSON
|
|
31
|
+
byte-identical to comparing a board against a copy of itself, while the raster
|
|
32
|
+
engine reported the change. The two engines answered the same question
|
|
33
|
+
differently and the geometry one gave the dangerous answer. Such operations are
|
|
34
|
+
now counted in `summary.unrepresented`, named on stderr, and make the outcome
|
|
35
|
+
`indeterminate` at exit `2` -- which does not wait for `--fail-on-diff`, since
|
|
36
|
+
that flag chooses whether a *difference* fails, not whether the tool could look.
|
|
37
|
+
`different` outranks `indeterminate`, so one unmodellable stroke cannot mask a
|
|
38
|
+
trace that moved.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **Draw operations using an undefined aperture are no longer dropped in
|
|
43
|
+
silence** ([#17]) -- selecting a D-code that was never defined (or drawing
|
|
44
|
+
before any `Dnn` selection at all) left the flash or stroke carrying an
|
|
45
|
+
aperture index with no definition behind it. Every consumer treated that
|
|
46
|
+
as nothing to draw: both geometry emit helpers returned early and the
|
|
47
|
+
raster renderer drew nothing, with no diagnostic at any severity. `diff`
|
|
48
|
+
and `geomdiff` then reported `0 changes` at exit `0`, and the JSON report
|
|
49
|
+
was byte-identical to a comparison of two genuinely identical boards -- so
|
|
50
|
+
a real fabrication change could pass a `--fail-on-diff` gate invisibly.
|
|
51
|
+
The parser now emits an `Error` diagnostic at the offending draw
|
|
52
|
+
operation, which the existing promotion path turns into exit `2` for
|
|
53
|
+
`parse`, `render`, `diff` and `geomdiff` alike. D02 moves and G36/G37
|
|
54
|
+
region contours consume no aperture and are unaffected.
|
|
55
|
+
|
|
56
|
+
- **Degenerate region contours no longer leak line geometry** -- a G36/G37
|
|
57
|
+
contour whose points are collinear produced a zero-area `MultiLineString`
|
|
58
|
+
from `make_valid` that flowed into the geometry engine; region expansion
|
|
59
|
+
now keeps only polygonal parts.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- Local coverage floor aligned with the CI gate (`fail_under` 85 -> 90;
|
|
64
|
+
actual coverage is ~94%).
|
|
65
|
+
- Stale documentation corrected: the overlay-PNG colour table no longer
|
|
66
|
+
lists the removed "yellow" class; `SECURITY.md` supported versions
|
|
67
|
+
updated from 0.14.x to 0.29.x; `CONTRIBUTING.md` blesses
|
|
68
|
+
conventional-commit prefixes (matching recent history).
|
|
69
|
+
- Package `__init__` modules for `parse`, `render`, `diff`, and `export`
|
|
70
|
+
gained one-line docstrings.
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- **Documentation site** at <https://heibench.github.io/gerberdiff/>
|
|
75
|
+
-- MkDocs + Material with mkdocstrings API reference, MathJax for the
|
|
76
|
+
LaTeX notation already used in the docs, and Changelog/Contributing
|
|
77
|
+
included via snippets. Built strictly and deployed to GitHub Pages by
|
|
78
|
+
`.github/workflows/docs.yml` on every push to `main` that touches docs
|
|
79
|
+
or the package.
|
|
80
|
+
|
|
81
|
+
- Edge-case test suite for the geometry engine's guard paths: block
|
|
82
|
+
nesting depth limit, invalid layer indices, zero-dimension apertures,
|
|
83
|
+
degenerate regions and strokes, macro flash dispatch, the
|
|
84
|
+
equal-geometry attribution guard, and driver diagnostic forwarding.
|
|
85
|
+
|
|
86
|
+
## [0.29.1] - 2026-06-13
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- **Package metadata reflects both engines** -- the PyPI `description`
|
|
91
|
+
and the CLI group help text described only the raster engine; both now
|
|
92
|
+
cover the raster overlay and geometry diff pipelines. Added `geometry`
|
|
93
|
+
and `shapely` to the package keywords. No behaviour changes.
|
|
94
|
+
|
|
10
95
|
## [0.29.0] - 2026-06-12
|
|
11
96
|
|
|
12
97
|
### Added
|
|
@@ -77,6 +162,103 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
77
162
|
Raster-engine tests skip cleanly when cairo is unavailable, which
|
|
78
163
|
fixes the previously failing Windows CI job.
|
|
79
164
|
|
|
165
|
+
- **Project renamed from `gerberdelta` to `gerberdiff`** (2026-04-25,
|
|
166
|
+
unreleased at the time). 0.29.0 is the first published release under
|
|
167
|
+
the new name; no release was ever made as `gerberdelta`. The rename
|
|
168
|
+
window also included: docs reorganised to lowercase-kebab filenames
|
|
169
|
+
with split CLI/API references, an ASCII-only character policy sweep,
|
|
170
|
+
LaTeX math notation for equations in docs, and strict mypy
|
|
171
|
+
annotations across the test suite.
|
|
172
|
+
|
|
173
|
+
## [0.28.0] - 2026-04-25
|
|
174
|
+
|
|
175
|
+
### Added
|
|
176
|
+
|
|
177
|
+
- **Render/diff regression coverage** -- 411 lines of new tests across
|
|
178
|
+
the renderer, draw-ops, diff engine, and parser: incremental (G91)
|
|
179
|
+
coordinates, step-and-repeat tiling, layer transforms (LM/LR/LS),
|
|
180
|
+
clear-polarity compositing, region merge cascade, polygon aperture
|
|
181
|
+
rotation, macro `unit_scale`, `alignment_offset`, and pixel-level
|
|
182
|
+
render verification. No production code changes.
|
|
183
|
+
|
|
184
|
+
## [0.27.0] - 2026-04-25
|
|
185
|
+
|
|
186
|
+
### Changed
|
|
187
|
+
|
|
188
|
+
- **`CompiledRender` cached per `BlockAperture`** -- repeated flashes of
|
|
189
|
+
the same block aperture no longer recompile its draw-op groups on
|
|
190
|
+
every flash. The cache is keyed by `id(block_ap)` and evicted by a
|
|
191
|
+
`weakref` finalizer when the aperture is garbage-collected, so stale
|
|
192
|
+
ids cannot produce false hits.
|
|
193
|
+
|
|
194
|
+
## [0.26.0] - 2026-04-25
|
|
195
|
+
|
|
196
|
+
### Fixed
|
|
197
|
+
|
|
198
|
+
- **Rect/obround stroke width** -- D01 strokes with rectangle or obround
|
|
199
|
+
apertures are drawn with line width `max(width, height)` instead of
|
|
200
|
+
`min(width, height)`, so traces whose long axis aligns with the stroke
|
|
201
|
+
direction (the common case) are no longer under-stroked; documented as
|
|
202
|
+
an approximation pending geometry-aware rendering.
|
|
203
|
+
- **Macro evaluation failures no longer abort the render** -- a macro
|
|
204
|
+
aperture whose primitive evaluation raises is skipped with a
|
|
205
|
+
`UserWarning` instead of crashing the whole render pass.
|
|
206
|
+
|
|
207
|
+
## [0.25.0] - 2026-04-25
|
|
208
|
+
|
|
209
|
+
### Fixed
|
|
210
|
+
|
|
211
|
+
- **Parser correctness sweep** --
|
|
212
|
+
- arc bounding boxes account for axis-extrema crossings (0/90/180/270
|
|
213
|
+
degrees), not just the endpoints;
|
|
214
|
+
- step-and-repeat tiles expand the image bounding box and respect
|
|
215
|
+
layer polarity;
|
|
216
|
+
- block aperture (`%AB%`) parsing isolates state correctly (nested
|
|
217
|
+
apertures/layers no longer leak into the parent image);
|
|
218
|
+
- `_apply_format` no longer truncates coordinates with more integer
|
|
219
|
+
digits than the format statement declares;
|
|
220
|
+
- referencing an unknown macro in an aperture definition is an
|
|
221
|
+
`Error`-severity diagnostic (was silently tolerated).
|
|
222
|
+
|
|
223
|
+
## [0.24.0] - 2026-04-25
|
|
224
|
+
|
|
225
|
+
### Changed
|
|
226
|
+
|
|
227
|
+
- **`DiffResult.has_changes` is a computed property** -- derived from the
|
|
228
|
+
layer results instead of stored at construction, so it cannot drift
|
|
229
|
+
from the data.
|
|
230
|
+
- **Excellon parser refactored** -- the nonlocal-closure state pattern is
|
|
231
|
+
replaced with explicit local parser state; behaviour unchanged.
|
|
232
|
+
|
|
233
|
+
## [0.23.0] - 2026-04-25
|
|
234
|
+
|
|
235
|
+
### Added
|
|
236
|
+
|
|
237
|
+
- **`compute_full_diff`** -- directory-vs-directory diff as a single
|
|
238
|
+
public API call (parse, match, diff, assemble `DiffResult`), with
|
|
239
|
+
`overlay_callback` and `on_diagnostic` hooks.
|
|
240
|
+
- **All IR and result types exported** from the top-level package with
|
|
241
|
+
an `__all__` list; README gains API examples.
|
|
242
|
+
|
|
243
|
+
### Changed
|
|
244
|
+
|
|
245
|
+
- `diff_cmd` rewritten on top of `compute_full_diff` (the CLI no longer
|
|
246
|
+
duplicates the orchestration logic).
|
|
247
|
+
|
|
248
|
+
## [0.22.0] - 2026-04-25
|
|
249
|
+
|
|
250
|
+
### Added
|
|
251
|
+
|
|
252
|
+
- **`docs/schema.md`** -- canonical JSON report schema documentation.
|
|
253
|
+
- **CI hardening** -- coverage gate (`--cov-fail-under=90`), a Windows
|
|
254
|
+
test job in the matrix, and a non-ASCII character check.
|
|
255
|
+
|
|
256
|
+
### Changed
|
|
257
|
+
|
|
258
|
+
- **`EXCELLON_SUFFIXES` is public** -- renamed from `_EXCELLON_SUFFIXES`
|
|
259
|
+
in `diff/layer_matcher.py`; it is part of the de-facto API used by the
|
|
260
|
+
CLI and downstream callers.
|
|
261
|
+
|
|
80
262
|
## [0.21.0] - 2026-04-25
|
|
81
263
|
|
|
82
264
|
### Changed
|
|
@@ -242,6 +424,10 @@ saved_bbox, saved_layer_idx)` with a named `_BlockFrame` dataclass so that
|
|
|
242
424
|
|
|
243
425
|
### Changed
|
|
244
426
|
|
|
427
|
+
- **License changed from AGPL-3.0 to Apache-2.0** so the tool can be
|
|
428
|
+
used commercially without copyleft obligations. (Landed immediately
|
|
429
|
+
before this version's other changes; recorded here for completeness.)
|
|
430
|
+
|
|
245
431
|
- **Domain model rename** -- `Net` renamed to `DrawOp` and `NetState` renamed to `CoordState`
|
|
246
432
|
throughout the codebase. The term "net" belongs to EDA net-list semantics; the IR types
|
|
247
433
|
represent drawing primitives and coordinate-system snapshots, not electrical nets.
|
|
@@ -529,19 +715,37 @@ merge_tolerance) -> SingleLayerDiff`.
|
|
|
529
715
|
- mypy `strict=true`, `warn_unused_ignores=true`, `cairocffi.*` override for missing stubs.
|
|
530
716
|
- 2 smoke tests in `tests/test_scaffold.py`.
|
|
531
717
|
|
|
532
|
-
[
|
|
533
|
-
[
|
|
534
|
-
[
|
|
535
|
-
[0.
|
|
536
|
-
[0.
|
|
537
|
-
[0.
|
|
538
|
-
[0.
|
|
539
|
-
[0.
|
|
540
|
-
[0.
|
|
541
|
-
[0.
|
|
542
|
-
[0.
|
|
543
|
-
[0.
|
|
544
|
-
[0.
|
|
545
|
-
[0.
|
|
546
|
-
[0.
|
|
547
|
-
[0.
|
|
718
|
+
[#17]: https://github.com/heibench/gerberdiff/issues/17
|
|
719
|
+
[A3]: https://heibench.com/adjudications.html
|
|
720
|
+
[Unreleased]: https://github.com/heibench/gerberdiff/compare/v0.30.0...HEAD
|
|
721
|
+
[0.30.0]: https://github.com/heibench/gerberdiff/compare/v0.29.1...v0.30.0
|
|
722
|
+
[0.29.1]: https://github.com/heibench/gerberdiff/compare/v0.29.0...v0.29.1
|
|
723
|
+
[0.29.0]: https://github.com/heibench/gerberdiff/compare/9ffd4c8f...v0.29.0
|
|
724
|
+
[0.28.0]: https://github.com/heibench/gerberdiff/compare/b6b6b98d...9ffd4c8f
|
|
725
|
+
[0.27.0]: https://github.com/heibench/gerberdiff/compare/963eb957...b6b6b98d
|
|
726
|
+
[0.26.0]: https://github.com/heibench/gerberdiff/compare/6162e435...963eb957
|
|
727
|
+
[0.25.0]: https://github.com/heibench/gerberdiff/compare/e2519ec8...6162e435
|
|
728
|
+
[0.24.0]: https://github.com/heibench/gerberdiff/compare/91c154d4...e2519ec8
|
|
729
|
+
[0.23.0]: https://github.com/heibench/gerberdiff/compare/458734f9...91c154d4
|
|
730
|
+
[0.22.0]: https://github.com/heibench/gerberdiff/compare/1c96a7b8...458734f9
|
|
731
|
+
[0.21.0]: https://github.com/heibench/gerberdiff/compare/4d1201fe...1c96a7b8
|
|
732
|
+
[0.20.0]: https://github.com/heibench/gerberdiff/compare/ba9a5015...4d1201fe
|
|
733
|
+
[0.19.0]: https://github.com/heibench/gerberdiff/compare/c502171a...ba9a5015
|
|
734
|
+
[0.18.0]: https://github.com/heibench/gerberdiff/compare/10f8f392...c502171a
|
|
735
|
+
[0.17.0]: https://github.com/heibench/gerberdiff/compare/b04813ea...10f8f392
|
|
736
|
+
[0.16.0]: https://github.com/heibench/gerberdiff/compare/3044033b...b04813ea
|
|
737
|
+
[0.15.0]: https://github.com/heibench/gerberdiff/compare/9b4e3401...3044033b
|
|
738
|
+
[0.14.0]: https://github.com/heibench/gerberdiff/compare/1a399ea2...9b4e3401
|
|
739
|
+
[0.13.0]: https://github.com/heibench/gerberdiff/compare/c0672ea8...1a399ea2
|
|
740
|
+
[0.12.0]: https://github.com/heibench/gerberdiff/compare/1bbfb235...c0672ea8
|
|
741
|
+
[0.11.0]: https://github.com/heibench/gerberdiff/compare/2d1573ad...1bbfb235
|
|
742
|
+
[0.10.0]: https://github.com/heibench/gerberdiff/compare/6691b195...2d1573ad
|
|
743
|
+
[0.9.0]: https://github.com/heibench/gerberdiff/compare/2e944edc...6691b195
|
|
744
|
+
[0.8.0]: https://github.com/heibench/gerberdiff/compare/46b463de...2e944edc
|
|
745
|
+
[0.7.0]: https://github.com/heibench/gerberdiff/compare/5b9212fa...46b463de
|
|
746
|
+
[0.6.0]: https://github.com/heibench/gerberdiff/compare/e12ff13f...5b9212fa
|
|
747
|
+
[0.5.0]: https://github.com/heibench/gerberdiff/compare/6330d0cb...e12ff13f
|
|
748
|
+
[0.4.0]: https://github.com/heibench/gerberdiff/compare/f63a51a3...6330d0cb
|
|
749
|
+
[0.3.0]: https://github.com/heibench/gerberdiff/compare/3f1d1909...f63a51a3
|
|
750
|
+
[0.2.0]: https://github.com/heibench/gerberdiff/compare/674251dd...3f1d1909
|
|
751
|
+
[0.1.0]: https://github.com/heibench/gerberdiff/compare/2eeb692...674251dd
|
|
@@ -7,7 +7,7 @@ Thank you for your interest in contributing to **gerberdiff**!
|
|
|
7
7
|
Requires Python >= 3.11 and [uv](https://docs.astral.sh/uv/).
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
git clone https://github.com/
|
|
10
|
+
git clone https://github.com/heibench/gerberdiff.git
|
|
11
11
|
cd gerberdiff
|
|
12
12
|
uv sync --dev
|
|
13
13
|
```
|
|
@@ -36,8 +36,11 @@ All three must pass with no errors before a PR will be merged. CI enforces this
|
|
|
36
36
|
|
|
37
37
|
## Commit messages
|
|
38
38
|
|
|
39
|
-
- Single
|
|
40
|
-
-
|
|
39
|
+
- Single-sentence subject, imperative mood, no trailing period
|
|
40
|
+
- [Conventional-commit](https://www.conventionalcommits.org/) type prefixes
|
|
41
|
+
(`feat:`, `fix:`, `docs:`, `test:`, `perf:`, `chore:`, `ci:`) are
|
|
42
|
+
encouraged and used throughout recent history
|
|
43
|
+
(e.g. `feat(geometry): add geometry diff engine core`)
|
|
41
44
|
|
|
42
45
|
## Pull request checklist
|
|
43
46
|
|
|
@@ -62,7 +65,7 @@ many fonts and meaningless to grep.
|
|
|
62
65
|
**Banned characters and their ASCII replacements:**
|
|
63
66
|
|
|
64
67
|
| Character | Codepoint | Use instead |
|
|
65
|
-
| ----------------- | --------- | ----------- |
|
|
68
|
+
| ----------------- | --------- | ----------- |
|
|
66
69
|
| em dash | U+2014 | `--` |
|
|
67
70
|
| en dash | U+2013 | `-` |
|
|
68
71
|
| ellipsis | U+2026 | `...` |
|
|
@@ -75,8 +78,8 @@ many fonts and meaningless to grep.
|
|
|
75
78
|
| almost-equal | U+2248 | `~=` |
|
|
76
79
|
| plus-minus | U+00B1 | `+/-` |
|
|
77
80
|
| degree | U+00B0 | `deg` |
|
|
78
|
-
| superscript 2 | U+00B2 | `^2`
|
|
79
|
-
| box-drawing chars | U+2500+ | `-`,
|
|
81
|
+
| superscript 2 | U+00B2 | `^2` |
|
|
82
|
+
| box-drawing chars | U+2500+ | `-`, `\|`, `+` |
|
|
80
83
|
|
|
81
84
|
To check a branch before committing:
|
|
82
85
|
|
|
@@ -106,4 +109,5 @@ EOF
|
|
|
106
109
|
## License
|
|
107
110
|
|
|
108
111
|
By contributing you agree that your contributions will be licensed under the
|
|
109
|
-
[Apache-2.0](LICENSE)
|
|
112
|
+
[Apache-2.0](https://github.com/heibench/gerberdiff/blob/main/LICENSE)
|
|
113
|
+
licence.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: gerberdiff
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Changelog, https://github.com/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
3
|
+
Version: 0.30.0
|
|
4
|
+
Summary: Diff tool for Gerber/Excellon PCB files: visual raster overlays and attributed vector-geometry change analysis
|
|
5
|
+
Project-URL: Homepage, https://github.com/heibench/gerberdiff
|
|
6
|
+
Project-URL: Repository, https://github.com/heibench/gerberdiff
|
|
7
|
+
Project-URL: Changelog, https://github.com/heibench/gerberdiff/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/heibench/gerberdiff/issues
|
|
9
9
|
Author-email: Cameron Brooks <cameronbrooks11@gmail.com>
|
|
10
10
|
License-Expression: Apache-2.0
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Keywords: diff,eda,excellon,gerber,grbl,kicad,pcb
|
|
12
|
+
Keywords: diff,eda,excellon,geometry,gerber,grbl,kicad,pcb,shapely
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Intended Audience :: Manufacturing
|
|
@@ -33,7 +33,8 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
|
|
34
34
|
# gerberdiff
|
|
35
35
|
|
|
36
|
-
[](https://github.com/heibench/gerberdiff/actions/workflows/ci.yml)
|
|
37
|
+
[](https://heibench.com/gerberdiff/)
|
|
37
38
|
[](https://pypi.org/project/gerberdiff/)
|
|
38
39
|
[](https://pypi.org/project/gerberdiff/)
|
|
39
40
|
[](LICENSE)
|
|
@@ -82,6 +83,8 @@ for layer in result.layers:
|
|
|
82
83
|
|
|
83
84
|
## Docs
|
|
84
85
|
|
|
86
|
+
Full documentation site: **<https://heibench.com/gerberdiff/>**
|
|
87
|
+
|
|
85
88
|
| Topic | File |
|
|
86
89
|
| -------------------- | -------------------------------------------- |
|
|
87
90
|
| CLI reference | [docs/cli.md](docs/cli.md) |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# gerberdiff
|
|
2
2
|
|
|
3
|
-
[](https://github.com/heibench/gerberdiff/actions/workflows/ci.yml)
|
|
4
|
+
[](https://heibench.com/gerberdiff/)
|
|
4
5
|
[](https://pypi.org/project/gerberdiff/)
|
|
5
6
|
[](https://pypi.org/project/gerberdiff/)
|
|
6
7
|
[](LICENSE)
|
|
@@ -49,6 +50,8 @@ for layer in result.layers:
|
|
|
49
50
|
|
|
50
51
|
## Docs
|
|
51
52
|
|
|
53
|
+
Full documentation site: **<https://heibench.com/gerberdiff/>**
|
|
54
|
+
|
|
52
55
|
| Topic | File |
|
|
53
56
|
| -------------------- | -------------------------------------------- |
|
|
54
57
|
| CLI reference | [docs/cli.md](docs/cli.md) |
|
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported versions
|
|
4
4
|
|
|
5
|
+
Only the latest released minor version is supported.
|
|
6
|
+
|
|
5
7
|
| Version | Supported |
|
|
6
8
|
| ------- | --------- |
|
|
7
|
-
| 0.
|
|
8
|
-
| < 0.
|
|
9
|
+
| 0.29.x | Yes |
|
|
10
|
+
| < 0.29 | No |
|
|
9
11
|
|
|
10
12
|
## Reporting a vulnerability
|
|
11
13
|
|
|
12
14
|
**Do not open a public issue for security vulnerabilities.**
|
|
13
15
|
|
|
14
16
|
Please report them via
|
|
15
|
-
[GitHub private security advisories](https://github.com/
|
|
17
|
+
[GitHub private security advisories](https://github.com/heibench/gerberdiff/security/advisories/new).
|
|
16
18
|
|
|
17
19
|
Include:
|
|
18
20
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--8<-- "CHANGELOG.md"
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## Exit codes
|
|
5
|
+
|
|
6
|
+
Shared across the org's tools, so a script can branch on them the same way for
|
|
7
|
+
`gerberdiff`, `partspec` and `netspec`.
|
|
8
|
+
|
|
9
|
+
| code | meaning |
|
|
10
|
+
| ---- | -------- |
|
|
11
|
+
| `0` | no differences, and everything was modelled |
|
|
12
|
+
| `1` | differences found (with `--fail-on-diff`) |
|
|
13
|
+
| `2` | part of the comparison could not be made -- **not** a statement about the boards |
|
|
14
|
+
| `4` | an input could not be read or parsed -- also not a statement about the boards |
|
|
15
|
+
| `64` | usage (`EX_USAGE`): bad arguments, or an output file that exists without `--overwrite` |
|
|
16
|
+
|
|
17
|
+
`2` does not wait for `--fail-on-diff`. That flag chooses whether a *difference* is a
|
|
18
|
+
failure; it has no bearing on whether the tool could look.
|
|
19
|
+
|
|
3
20
|
## `parse` -- inspect a single file
|
|
4
21
|
|
|
5
22
|
```sh
|
|
@@ -49,7 +66,6 @@ gerberdiff diff before/ after/ --align-offset 0.5,0 # shift board B by 0.5 in
|
|
|
49
66
|
| ------ | -------------------------------------------------- |
|
|
50
67
|
| Red | Geometry present in **before** only (removed) |
|
|
51
68
|
| Green | Geometry present in **after** only (added) |
|
|
52
|
-
| Yellow | Geometry changed (both non-zero, different value) |
|
|
53
69
|
| Grey | Unchanged geometry (only with `--png-show-common`) |
|
|
54
70
|
|
|
55
71
|
## `geomdiff` -- geometry-aware diff of two layer directories
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--8<-- "CONTRIBUTING.md"
|
|
@@ -158,8 +158,14 @@ documented so the two engines' outputs are interpreted correctly:
|
|
|
158
158
|
|
|
159
159
|
## Known limitations
|
|
160
160
|
|
|
161
|
-
- Strokes drawn with macro or block apertures are
|
|
161
|
+
- Strokes drawn with macro or block apertures are not modelled (the raster
|
|
162
162
|
engine draws them as hairlines; neither engine models them properly).
|
|
163
|
+
**They are reported, not skipped in silence.** Each one is counted in
|
|
164
|
+
`summary.unrepresented`, makes the layer `indeterminate`, and gives the
|
|
165
|
+
comparison an `outcome` of `"indeterminate"` at exit `2`. This limitation was
|
|
166
|
+
documented here long before the output admitted to it: a board with such a
|
|
167
|
+
stroke reported `0 changes` at exit `0`, with JSON byte-identical to comparing
|
|
168
|
+
a board against a copy of itself, while the raster engine reported the change.
|
|
163
169
|
- `rerouted` classification is deferred: a redrawn trace reports as
|
|
164
170
|
removed + added segments (or `resized` when endpoints stay close).
|
|
165
171
|
- Geometry changes are op-granular: a moved multi-op footprint reports
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# gerberdiff
|
|
2
|
+
|
|
3
|
+
Diff tool for Gerber/Excellon PCB design files, with two complementary
|
|
4
|
+
engines:
|
|
5
|
+
|
|
6
|
+
- **Raster diff** (`diff`) -- renders both revisions with Cairo and XORs the
|
|
7
|
+
pixels; produces visual overlay PNGs of changed regions.
|
|
8
|
+
- **Geometry diff** (`geomdiff`) -- computes resolution-independent,
|
|
9
|
+
**attributed** changes on the parsed vector geometry: every change is
|
|
10
|
+
classified as `added`, `removed`, `moved` (with dx/dy displacement, down
|
|
11
|
+
to micrometres), or `resized`, with net names propagated from `%TO.N%`
|
|
12
|
+
attributes.
|
|
13
|
+
|
|
14
|
+
The raster engine answers "*where* did pixels change?"; the geometry engine
|
|
15
|
+
answers "*what* changed, and by how much?". A $0.14\,\text{mm}$ component
|
|
16
|
+
move that renders as an unreadable field of XOR crescents in a raster diff
|
|
17
|
+
is reported by the geometry engine as "46 objects moved by
|
|
18
|
+
$(-0.139, -0.054)\,\text{mm}$".
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pip install gerberdiff
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Python >= 3.11. The raster engine needs the system Cairo library
|
|
27
|
+
(`libcairo2` on Debian/Ubuntu, `cairo` via Homebrew); the geometry engine
|
|
28
|
+
and the parsers are Cairo-free and work everywhere.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
# Geometry diff: what moved, resized, was added or removed -- and by how much
|
|
34
|
+
gerberdiff geomdiff before/ after/ --out-json report.json --out-svg overlays/
|
|
35
|
+
|
|
36
|
+
# Raster diff: visual overlay PNGs
|
|
37
|
+
gerberdiff diff before/ after/ --out-json report.json --out-png diffs/
|
|
38
|
+
|
|
39
|
+
# Exit 1 if any changes detected (useful in CI)
|
|
40
|
+
gerberdiff geomdiff before/ after/ --fail-on-diff
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import gerberdiff
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
result = gerberdiff.compute_geometry_diff(Path("before/"), Path("after/"))
|
|
48
|
+
for layer in result.layers:
|
|
49
|
+
for change in layer.changes:
|
|
50
|
+
print(f"{layer.name}: {change.kind} {change.op_kind} "
|
|
51
|
+
f"dx={change.dx_mm} dy={change.dy_mm}")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Where to go next
|
|
55
|
+
|
|
56
|
+
| I want to... | Read |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| Use the command line | [CLI reference](cli.md) |
|
|
59
|
+
| Call it from Python | [Python API](api.md) |
|
|
60
|
+
| Parse the JSON reports | [JSON report schemas](schema.md) |
|
|
61
|
+
| Understand how it works | [Architecture](architecture.md) |
|
|
62
|
+
| Deep-dive the geometry engine | [Geometry diff engine](geometry-diff.md) |
|
|
63
|
+
| Browse the API surface | [API reference](reference/package.md) |
|
|
64
|
+
| Contribute | [Contributing](contributing.md) |
|
|
65
|
+
|
|
66
|
+
## Known limitations
|
|
67
|
+
|
|
68
|
+
- **Excellon rout mode:** only drill hits are processed; routing paths
|
|
69
|
+
produce a `Warning` diagnostic but no geometry.
|
|
70
|
+
- **Deprecated RS-274X commands (`%MI%`, `%OF%`, `%SF%`, `%AS%`):** ignored
|
|
71
|
+
with an `Info` diagnostic.
|
|
72
|
+
- **Rectangle/obround aperture strokes:** the raster engine strokes with
|
|
73
|
+
`max(width, height)`; the geometry engine computes the exact Minkowski sum
|
|
74
|
+
for linear strokes (see [Geometry diff engine](geometry-diff.md)).
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
[Apache-2.0](https://github.com/heibench/gerberdiff/blob/main/LICENSE).
|