Installation -> Quick start, and the build commands move into it so the file ends on Layout then More like every other repo.
120 lines
4.4 KiB
Markdown
120 lines
4.4 KiB
Markdown
# n3xd-ocp
|
|
|
|
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.
|
|
|
|
## 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).
|
|
|
|
## Quick start
|
|
|
|
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 `<occt-version>.N`, enforced at configure time against the OCCT
|
|
actually found.
|
|
|
|
Wheels are built on a dev box and published to that registry. To build one
|
|
yourself, everything runs in the OCCT builder image, so Docker is the only
|
|
prerequisite:
|
|
|
|
```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
|
|
```
|
|
|
|
## Usage
|
|
|
|
`OCP` mirrors `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) 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 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
|
|
|
|
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()
|
|
```
|
|
|
|
## 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 |
|
|
|
|
## 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.
|