# 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`. 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. ## Installation As packages sit on our Gitea instance for, you must install by providing the specific url, like: ```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. ## Usage `OCP` mirrors [`cadquery-ocp`](https://github.com/cadquery/OCP) symbol-for-symbol, so code written against it runs unchanged: ```python from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut from OCP.TopTools import TopTools_ListOfShape box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape() hole = BRepPrimAPI_MakeBox(3.0, 3.0, 30.0).Shape() args, tools = TopTools_ListOfShape(), TopTools_ListOfShape() args.Append(box) tools.Append(hole) cut = BRepAlgoAPI_Cut() cut.SetArguments(args) cut.SetTools(tools) 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. `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: ```python import n3xd_ocp areas, centroids = n3xd_ocp.measure.face_surface_props(result) # one call for every face meshes = n3xd_ocp.tess.extract_meshes(result) # triangulated faces, ready to render data = n3xd_ocp.bintools.write_bytes(result) # BREP bytes, no temp file needed 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: ```python from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt axis = gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1), gp_Dir(1, 0, 0)) wire, tolerance_reached = n3xd_ocp.helix.pure_helix(axis, 8.0, [1.25], [12.0]) builder = n3xd_ocp.helix.BuilderHelix() # tapered, e.g. an NPT thread builder.set_parameters(axis, [10.0, 8.0], [2.0], [4.0]) builder.set_approx_parameters(1.0e-4) builder.perform() ``` ## 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: ```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 ```