diff --git a/README.md b/README.md index b013323..307f302 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,36 @@ # n3xd-ocp -Hand-written [nanobind](https://github.com/wjakob/nanobind) wrapper for the OpenCASCADE (OCCT) geometry kernel. -In comparison to [`cadquery-ocp](https://github.com/cadquery/OCP), we get superlinear 7.0x speedup on the use cases in `n3xd`. +Hand-written [nanobind](https://github.com/wjakob/nanobind) wrapper for the +OpenCASCADE (OCCT) geometry kernel. It installs as a top-level `OCP` and is a +drop-in replacement for [`cadquery-ocp`](https://github.com/cadquery/OCP): same +symbols, same call shapes. What it buys over upstream is version velocity (the +kernel is ours to bump), a much smaller footprint, correctness around object +ownership, and room to add APIs upstream does not offer, such as releasing the +GIL and extracting whole shapes in one call. -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. +## Status + +The binding covers the subset of OCCT that `n3xd` actually uses, not the whole +kernel; run `tools/inventory.py` for the current count. Adding a class is a +routine, documented change, see +[docs/adding-symbols.md](docs/adding-symbols.md). ## Installation -As packages sit on our Gitea instance for, you must install by providing the specific url, like: +The packages sit on our Gitea instance, so install by pointing at its index: ```bash uv pip install --index-url https://git.stroblme.de/api/packages/N3XD/pypi/simple/ \ --prerelease=allow n3xd-ocp ``` -Versions are `.N`, enforced at configure time against the OCCT actually found. +Versions are `.N`, enforced at configure time against the OCCT +actually found. ## Usage -`OCP` mirrors [`cadquery-ocp`](https://github.com/cadquery/OCP) symbol-for-symbol, so code written against it runs unchanged: +`OCP` mirrors `cadquery-ocp` symbol-for-symbol, so code written against it runs +unchanged: ```python from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox @@ -41,9 +51,16 @@ cut.Build() result = cut.Shape() ``` -One deliberate gap from upstream: constructors that run the algorithm immediately (the two-argument `BRepAlgoAPI_Cut(a, b)` form) aren't bound, only the deferred `SetArguments`/`SetTools`/`Build()` sequence above. See [docs/design.md](docs/design.md) for why. +One deliberate gap from upstream: constructors that run the algorithm +immediately (the two-argument `BRepAlgoAPI_Cut(a, b)` form) are not bound, only +the deferred `SetArguments`/`SetTools`/`Build()` sequence above. See +[docs/design.md](docs/design.md) for why. -`n3xd_ocp` adds a handful of batch operations OCP doesn't have. They run on the same OCCT build and take/return plain `OCP` shapes: +`n3xd_ocp` adds a handful of batch operations OCP does not have. They run on the +same OCCT build and take and return plain `OCP` shapes, and they exist because +doing the same work as a per-face Python loop is measurably slower (face +extraction came out 4.6-5.5x faster on ordinary geometry when the app adopted +it): ```python import n3xd_ocp @@ -55,7 +72,9 @@ data = n3xd_ocp.bintools.write_bytes(result) # BREP bytes, n points, normals, uv_bounds = n3xd_ocp.sample.face_grid(face, 33) # a 33x33 UV grid on one face ``` -`n3xd_ocp.helix` reaches OCCT 8.0's TKHelix, which upstream has no binding for. For N segments it wants N pitches, N turn counts and **N+1 diameters** — one per segment boundary, so consecutive values that differ taper across that segment: +`n3xd_ocp.helix` reaches OCCT 8.0's TKHelix, which upstream has no binding for. +For N segments it wants N pitches, N turn counts and **N+1 diameters**, one per +segment boundary, so consecutive values that differ taper across that segment: ```python from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt @@ -69,15 +88,34 @@ builder.set_approx_parameters(1.0e-4) builder.perform() ``` +## Layout + +| Path | What it is | +| --- | --- | +| `src/modules/` | The bound OCCT classes, one file per OCCT module | +| `src/ext/` | The `n3xd_ocp` extras: batch measure, tessellation, sampling, helix | +| `src/common/` | Handle and transient casters, the machinery every module relies on | +| `python/OCP/`, `python/n3xd_ocp/` | The Python packages and their type stubs | +| `occt/` | The Dockerfile that compiles OCCT 8.0.1 into the builder image | +| `tools/` | Inventory, signature diffing against upstream, and benchmarks | + ## Build -Wheels are built on a dev box and published to the Gitea package registry [here](https://git.stroblme.de/api/packages/N3XD/pypi). - -If you want to make modifications or build it yourself, here are some shortcuts: +Wheels are built on a dev box and published to the +[Gitea package registry](https://git.stroblme.de/api/packages/N3XD/pypi). To +build one yourself: ```bash make image # compiles OCCT 8.0.1 into the builder image make dev # incremental compile + tests make wheel # compile, stubs, auditwheel, self-containment smoke test make publish # publish to Gitea using .secret credentials -``` \ No newline at end of file +``` + +## More + +- [docs/design.md](docs/design.md) — the decisions and the handle model +- [docs/building.md](docs/building.md) — the builder image and the wheel +- [docs/adding-symbols.md](docs/adding-symbols.md) — extending the surface + +Licensed LGPL-2.1-only, following OCCT.