138 symbols, 53 modules, published as 7.9.3.1.dev5. The app's full suite passes against it and the whole project store sweeps identical. Corrects the "call overhead is not a bottleneck" claim: it is true of any single call and false of the aggregate. The app's benchmark suite runs 194 s -> 73 s with nothing but the wheel swapped, and the win grows with face count — 3.3x at 4 features, 7.0x at 32. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DfriM8XUkn7uYf5Dwe2xo6
75 lines
3.6 KiB
Markdown
75 lines
3.6 KiB
Markdown
# n3xd-ocp
|
|
|
|
Hand-written [nanobind](https://github.com/wjakob/nanobind) bindings for the
|
|
OpenCASCADE (OCCT) geometry kernel, covering exactly the surface the N3XD CAD
|
|
backend uses — 138 symbols across 47 `OCP.*` modules, not all of OCCT.
|
|
|
|
The package installs as a top-level `OCP`, so it is a drop-in replacement for
|
|
`cadquery-ocp-novtk` and the app's 442 import sites stay untouched.
|
|
|
|
Status: **coverage complete** — all 138 symbols the app imports, across 53
|
|
bound modules, published as `7.9.3.1.dev5`. The app's full backend suite passes
|
|
against it (1797 passed / 1 skipped, the same as the stock wheel), a sweep of
|
|
the whole project store reproduces every part's geometry exactly (4486 parts;
|
|
no change in statuses, volume, area, bbox, entity counts, triangles or anchor
|
|
digests), and BREP serialisation stays byte-identical, which the pools and the
|
|
content-addressed derive payloads depend on. The app is not swapped yet — that
|
|
is the cutover, roadmap 10C.
|
|
|
|
Start with [docs/design.md](docs/design.md) for the decisions,
|
|
[docs/building.md](docs/building.md) to build one, and
|
|
[docs/adding-symbols.md](docs/adding-symbols.md) to extend the surface. The
|
|
phase plan lives in the app repo at `docs-private/reference/roadmap.md`
|
|
(Phase 10).
|
|
|
|
## Why
|
|
|
|
`cadquery-ocp` lags OCCT (it wraps 7.9.3; OCCT 8.0 shipped 2026-05), builds
|
|
Windows and macOS wheels we never use, and until recently forced a 638 MB VTK
|
|
dependency into the image. So this exists for version velocity, footprint, and
|
|
two defects that a binding we control prevents by construction:
|
|
|
|
- OCCT sub-shapes are returned **by value**, so a wrapper can never alias a
|
|
`TShape` whose owner has died (this segfaulted a process-global face memo).
|
|
- Executing constructors (the two-argument `BRepAlgoAPI_*` forms) are **not
|
|
bound**, so the double-execution footgun is unrepresentable.
|
|
|
|
It also releases the GIL around kernel calls and ships type stubs, neither of
|
|
which upstream does.
|
|
|
|
**It is also considerably faster, which was not the point and turned out to
|
|
matter most.** With the app otherwise unchanged, its benchmark suite runs
|
|
194 s → 73 s, and rebuild time improves *superlinearly* with model complexity:
|
|
3.3x for a 4-feature part, 7.0x for a 32-feature one (13.4 s → 1.9 s). The
|
|
premise going in was that call overhead is irrelevant because the hotspots live
|
|
inside the kernel — true of any single call, false of the aggregate, because
|
|
this backend reaches OCCT once per face, per node and per edge.
|
|
`tools/bench_ext.py` has the numbers and the two places they contradicted the
|
|
plan.
|
|
|
|
## Build
|
|
|
|
OCCT is compiled once into a builder image and reused; it is never built on the
|
|
production host (4 cores, and a kernel build is multi-hour). Wheels are built
|
|
here on a dev box and published to the Gitea package registry.
|
|
|
|
```bash
|
|
make image # once, ~40 min: compiles OCCT 7.9.3 into the builder image
|
|
make dev # inner loop: incremental compile + tests
|
|
make wheel # compile, stubs, auditwheel, self-containment smoke test
|
|
make publish # -> https://git.stroblme.de/api/packages/N3XD/pypi
|
|
```
|
|
|
|
Credentials go in `.secrets` (gitignored) as `UV_PUBLISH_USERNAME` /
|
|
`UV_PUBLISH_PASSWORD`. Consumers read anonymously — the package is public:
|
|
|
|
```bash
|
|
uv pip install --index-url https://git.stroblme.de/api/packages/N3XD/pypi/simple/ \
|
|
--prerelease=allow n3xd-ocp
|
|
```
|
|
|
|
Versions are `<occt-version>.N`, enforced at configure time against the OCCT
|
|
actually found, so the kernel a wheel wraps is readable from its version alone.
|
|
The registry refuses to republish a version; iteration builds therefore carry a
|
|
`.devN` suffix and are the only ones the registry's cleanup rule collects.
|