Phase 10A/10B Inc 0: build system, handle model, first module surface

Builds n3xd-ocp end to end and publishes 7.9.3.1.dev1 to the Gitea registry,
where it installs anonymously and passes its suite.

- occt/Dockerfile: OCCT 7.9.3 compiled once into a manylinux_2_28 builder
  image (base digest + tarball sha256 pinned), Draw/VTK/Tk/Xlib/OpenGL off,
  FreeType on, -O2 without fast-math or march=native. A final layer asserts
  TKService/TKV3d exist with no libGL/libX11 DT_NEEDED, which is what lets the
  app image drop libgl1/libx11-6. Mounted into, never built FROM.
- scikit-build-core + nanobind STABLE_ABI -> one cp312-abi3 extension that
  registers every OCP.* submodule via PyImport_AddModule, so `import
  OCP.TopoDS` needs no shim and cls.__module__ is right. Version <occt>.N is
  asserted against the OCCT found, keeping occt_version() truthful.
- occt_handle.h: type caster for opencascade::handle<T> over OCCT's intrusive
  refcount. Wrappers are non-owning instances holding exactly one handle in
  their keep-alive list, reusing an existing wrapper so identity survives a
  round trip. Transient constructors go through ocp_new (never nb::init<>,
  which would let OCCT delete nanobind's storage); the caster refuses a
  refcount-0 object rather than corrupt the heap. Verified under ASAN with no
  memory-safety errors, plus an RSS bound over 50k create/destroy cycles.
- Sub-shapes are returned by value everywhere, making the TShape lifetime class
  that segfaulted a process-global face memo unrepresentable.
- Standard_Failure derives RuntimeError, with ~20 concrete types dispatched on
  the dynamic OCCT type (cad_pool marshals failures home by type name).
- Inc 0 surface: gp subset, TopAbs, TopoDS (+ downcasts), TopExp, TopLoc,
  TopTools, BRep, BinTools, Poly, Standard. 34 of the app's 139 symbols.
- n3xd_ocp: additive APIs kept out of the OCP namespace so parity testing stays
  meaningful. bintools (shape <-> bytes, GIL-free, byte-identical) and _debug.

Two findings worth the record, both verified against the stock wheel rather
than assumed: upstream binds __hash__ but leaves __eq__ at identity, which is
exactly what geom_memo.py's hash-bucket + IsSame scan is built around, so we
match it instead of "fixing" it; and BinTools can release the GIL after all, by
slurping the file-like object instead of bridging a streambuf that would call
back into Python.

Gate: BREP round-trips are byte-identical to cadquery-ocp-novtk across six
fixtures (the generator asserts stock idempotency first). That matters beyond
IPC — derive.py content-addresses BREP payloads by sha256 and stores the ref.
This commit is contained in:
2026-08-10 16:10:28 +02:00
parent b757d6e8d6
commit 6139852768
58 changed files with 3446 additions and 13 deletions

291
inventory.json Normal file
View File

@@ -0,0 +1,291 @@
{
"app_root": "/home/stroblme/n3xd/app/backend",
"files_importing_ocp": 115,
"modules": {
"BOPAlgo": {
"BOPAlgo_GlueShift": []
},
"BRep": {
"BRep_Builder": [],
"BRep_Tool": [
"Degenerated_s",
"Pnt_s",
"Surface_s",
"Tolerance_s",
"Triangulation_s"
]
},
"BRepAdaptor": {
"BRepAdaptor_Curve": [],
"BRepAdaptor_Surface": []
},
"BRepAlgoAPI": {
"BRepAlgoAPI_Common": [],
"BRepAlgoAPI_Cut": [],
"BRepAlgoAPI_Fuse": [],
"BRepAlgoAPI_Section": [],
"BRepAlgoAPI_Splitter": []
},
"BRepBndLib": {
"BRepBndLib": [
"Add_s"
]
},
"BRepBuilderAPI": {
"BRepBuilderAPI_Copy": [],
"BRepBuilderAPI_MakeEdge": [],
"BRepBuilderAPI_MakeFace": [],
"BRepBuilderAPI_MakePolygon": [],
"BRepBuilderAPI_MakeWire": [],
"BRepBuilderAPI_Sewing": [],
"BRepBuilderAPI_Transform": []
},
"BRepCheck": {
"BRepCheck_Analyzer": [],
"BRepCheck_Status": [
"BRepCheck_IntersectingWires",
"BRepCheck_SelfIntersectingWire"
]
},
"BRepClass3d": {
"BRepClass3d_SolidClassifier": []
},
"BRepExtrema": {
"BRepExtrema_DistShapeShape": []
},
"BRepFilletAPI": {
"BRepFilletAPI_MakeChamfer": [],
"BRepFilletAPI_MakeFillet": []
},
"BRepGProp": {
"BRepGProp": [
"LinearProperties_s",
"SurfaceProperties_s",
"VolumeProperties_s"
]
},
"BRepLib": {
"BRepLib": [
"BuildCurves3d_s",
"OrientClosedSolid_s"
]
},
"BRepMesh": {
"BRepMesh_IncrementalMesh": []
},
"BRepOffsetAPI": {
"BRepOffsetAPI_DraftAngle": [],
"BRepOffsetAPI_MakePipe": [],
"BRepOffsetAPI_MakePipeShell": [],
"BRepOffsetAPI_MakeThickSolid": [],
"BRepOffsetAPI_ThruSections": []
},
"BRepPrimAPI": {
"BRepPrimAPI_MakeBox": [],
"BRepPrimAPI_MakeCone": [],
"BRepPrimAPI_MakeCylinder": [],
"BRepPrimAPI_MakeHalfSpace": [],
"BRepPrimAPI_MakePrism": [],
"BRepPrimAPI_MakeRevol": []
},
"BRepTools": {
"BRepTools": [
"Clean_s",
"OuterWire_s",
"UVBounds_s"
],
"BRepTools_WireExplorer": []
},
"BinTools": {
"BinTools": [
"Read_s",
"Write_s"
]
},
"Bnd": {
"Bnd_Box": []
},
"GC": {
"GC_MakeArcOfCircle": []
},
"GCE2d": {
"GCE2d_MakeLine": [],
"GCE2d_MakeSegment": []
},
"GCPnts": {
"GCPnts_AbscissaPoint": [
"Length_s"
],
"GCPnts_TangentialDeflection": [],
"GCPnts_UniformDeflection": []
},
"GProp": {
"GProp_GProps": []
},
"Geom": {
"Geom_BSplineCurve": [],
"Geom_CylindricalSurface": [],
"Geom_OffsetSurface": []
},
"GeomAPI": {
"GeomAPI_PointsToBSplineSurface": [],
"GeomAPI_ProjectPointOnSurf": []
},
"GeomAbs": {
"GeomAbs_BSplineCurve": [],
"GeomAbs_BSplineSurface": [],
"GeomAbs_BezierCurve": [],
"GeomAbs_BezierSurface": [],
"GeomAbs_C2": [],
"GeomAbs_Circle": [],
"GeomAbs_Cone": [],
"GeomAbs_Cylinder": [],
"GeomAbs_Ellipse": [],
"GeomAbs_Line": [],
"GeomAbs_Plane": [],
"GeomAbs_Sphere": [],
"GeomAbs_Torus": []
},
"GeomLProp": {
"GeomLProp_SLProps": []
},
"Graphic3d": {
"Graphic3d_HTA_CENTER": [],
"Graphic3d_HTA_LEFT": [],
"Graphic3d_HTA_RIGHT": [],
"Graphic3d_VTA_BOTTOM": [],
"Graphic3d_VTA_CENTER": [],
"Graphic3d_VTA_TOP": []
},
"IFSelect": {
"IFSelect_RetDone": []
},
"IGESControl": {
"IGESControl_Controller": [
"Init_s"
],
"IGESControl_Reader": [],
"IGESControl_Writer": []
},
"IntCurvesFace": {
"IntCurvesFace_ShapeIntersector": []
},
"Interface": {
"Interface_Static": [
"CVal_s",
"SetCVal_s"
]
},
"NCollection": {
"NCollection_Utf8String": []
},
"Poly": {
"Poly_Triangulation": []
},
"RWStl": {
"RWStl": [
"ReadFile_s"
]
},
"STEPControl": {
"STEPControl_AsIs": [],
"STEPControl_Controller": [
"Init_s"
],
"STEPControl_Reader": [],
"STEPControl_Writer": []
},
"ShapeAnalysis": {
"ShapeAnalysis_FreeBounds": [
"ConnectEdgesToWires_s"
]
},
"ShapeFix": {
"ShapeFix_Face": [],
"ShapeFix_Shape": [],
"ShapeFix_Solid": []
},
"ShapeUpgrade": {
"ShapeUpgrade_UnifySameDomain": []
},
"StdPrs": {
"StdPrs_BRepFont": [],
"StdPrs_BRepTextBuilder": []
},
"StlAPI": {
"StlAPI_Writer": []
},
"TColStd": {
"TColStd_Array1OfInteger": [],
"TColStd_Array1OfReal": [],
"TColStd_SequenceOfAsciiString": []
},
"TColgp": {
"TColgp_Array1OfPnt": [],
"TColgp_Array2OfPnt": []
},
"TopAbs": {
"TopAbs_EDGE": [],
"TopAbs_FACE": [],
"TopAbs_IN": [],
"TopAbs_ON": [],
"TopAbs_OUT": [],
"TopAbs_REVERSED": [],
"TopAbs_SHELL": [],
"TopAbs_SOLID": [],
"TopAbs_ShapeEnum": [
"TopAbs_SHELL"
],
"TopAbs_VERTEX": [],
"TopAbs_WIRE": []
},
"TopExp": {
"TopExp": [
"MapShapesAndAncestors_s",
"MapShapes_s"
],
"TopExp_Explorer": []
},
"TopLoc": {
"TopLoc_Location": []
},
"TopTools": {
"TopTools_HSequenceOfShape": [],
"TopTools_IndexedDataMapOfShapeListOfShape": [],
"TopTools_IndexedMapOfShape": [],
"TopTools_ListOfShape": []
},
"TopoDS": {
"TopoDS": [
"Edge_s",
"Face_s",
"Shell_s",
"Solid_s",
"Vertex_s",
"Wire_s"
],
"TopoDS_Compound": [],
"TopoDS_Edge": [],
"TopoDS_Face": [],
"TopoDS_Shape": [],
"TopoDS_Shell": [],
"TopoDS_Wire": []
},
"gp": {
"gp_Ax1": [],
"gp_Ax2": [],
"gp_Ax3": [],
"gp_Circ": [],
"gp_Dir": [],
"gp_Dir2d": [],
"gp_Identity": [],
"gp_Lin": [],
"gp_Pln": [],
"gp_Pnt": [],
"gp_Pnt2d": [],
"gp_Quaternion": [],
"gp_Trsf": [],
"gp_Vec": []
}
}
}