stroblme ab32514df1 up
Signed-off-by: stroblme <stroblme@posteo.de>
2026-08-11 15:07:42 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00
2026-08-10 19:52:58 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00
2026-08-11 12:43:54 +02:00

n3xd-ocp

Hand-written 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: in production use — all 138 symbols the app imports, across 53 bound modules, published as 8.0.1.1 (OCCT 8.0.1). The app cut over on 2026-08-10 at 7.9.3.1 and took the kernel bump on 2026-08-11; its full backend suite passes against both (1798 passed / 1 skipped, the same as the stock wheel it replaced), 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 — across the kernel bump as well, so nothing stored had to be rewritten.

Start with docs/design.md for the decisions, docs/building.md to build one, and 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 still wraps 7.9.3; we are on 8.0.1), 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.

make image    # once, ~40 min: compiles OCCT 8.0.1 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:

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.

Description
No description provided
Readme 273 KiB
Languages
C++ 59.6%
Python 34.2%
Shell 3.2%
Dockerfile 1.2%
CMake 0.9%
Other 0.9%