10C Inc 1: core modeling
Binds the modeling core the app builds every feature out of — 93 of its 138 symbols now resolve, up from 34. New modules: GeomAbs, Geom2d, Geom, TCollection, TColgp, TColStd, GProp, BRepGProp, Bnd, BRepBndLib, Adaptor3d, BRepAdaptor, GeomLProp, GeomAPI, BRepBuilderAPI, BOPAlgo, BRepAlgoAPI, BRepPrimAPI, GC, BRepMesh; gp and BRep_Tool completed. Three structural decisions: - BRepBuilderAPI_MakeShape carries Build/Shape/Generated/Modified/IsDeleted for every maker in the binding, so the booleans, the primitives and (later) the fillet builders all answer the app's duck-typed provenance layer through ordinary virtual dispatch. History lists come back copied, so they outlive the builder. - The executing two-argument BRepAlgoAPI constructors stay unbound; operands go in through SetArguments/SetTools. Section keeps Init1/Init2, which are plain setters. BOPAlgo moved up from Inc 2 — SetGlue needs its enum. - Adaptor3d is registered although the app never imports it: every method it calls on BRepAdaptor_Curve/Surface is a virtual declared there, so binding them once on the bases leaves mod_BRepAdaptor.cpp with just constructors. Gate: tests/test_inc1_modeling.py against reference values gen_fixtures.py now records from the stock wheel — measurements, per-face area/centroid in map order, mesh counts, and the boolean history map compared exactly, since that is the substrate the app's topological naming is built on. tools/sigdiff.py compares our bound surface against stock's, because a wrong nb::init<> is silent: MakePrism's five-argument form bound OCCT's semi-infinite gp_Dir overload (gp_Dir converts from gp_Vec), producing a valid solid of the wrong shape with the flags shifted along. The fixture digest caught it; sigdiff finds the class of bug directly, and now reports only one deliberate deviation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DfriM8XUkn7uYf5Dwe2xo6
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,3 +35,6 @@ third_party/
|
|||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Stray venv/lock from running uv inside this repo (it is not a uv project)
|
||||||
|
uv.lock
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ target_link_libraries(_OCP PRIVATE
|
|||||||
TKGeomBase
|
TKGeomBase
|
||||||
TKBRep
|
TKBRep
|
||||||
TKTopAlgo
|
TKTopAlgo
|
||||||
|
# Inc 1
|
||||||
|
TKGeomAlgo # GeomAPI, GCPnts
|
||||||
|
TKPrim # BRepPrimAPI
|
||||||
|
TKBO # BRepAlgoAPI, BOPAlgo
|
||||||
|
TKMesh # BRepMesh
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(_OCP PRIVATE
|
target_compile_definitions(_OCP PRIVATE
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -32,6 +32,18 @@ dev: ## Incremental build + run the test suite (the inner loop)
|
|||||||
|
|
||||||
test: dev ## Alias for dev
|
test: dev ## Alias for dev
|
||||||
|
|
||||||
|
check: ## Coverage of the app's symbol inventory, against the in-container build
|
||||||
|
$(DOCKER_RUN) /cache/venv/bin/python /io/tools/inventory.py --check \
|
||||||
|
--inventory /io/inventory.json
|
||||||
|
|
||||||
|
sigdiff: ## Diff our bound surface against the stock wheel's (needs the app venv)
|
||||||
|
cd $(REPO)/../app && uv run --project backend python \
|
||||||
|
$(REPO)/tools/sigdiff.py --dump $(CACHE)/sig-stock.json
|
||||||
|
$(DOCKER_RUN) /cache/venv/bin/python /io/tools/sigdiff.py \
|
||||||
|
--dump /cache/sig-ours.json
|
||||||
|
python3 $(REPO)/tools/sigdiff.py --compare \
|
||||||
|
$(CACHE)/sig-stock.json $(CACHE)/sig-ours.json
|
||||||
|
|
||||||
shell: ## Interactive shell in the builder image
|
shell: ## Interactive shell in the builder image
|
||||||
docker run --rm -it -v $(REPO):/io -v $(CACHE):/cache $(IMAGE) bash
|
docker run --rm -it -v $(REPO):/io -v $(CACHE):/cache $(IMAGE) bash
|
||||||
|
|
||||||
|
|||||||
@@ -75,9 +75,24 @@ only in that a base class must precede its derived classes.
|
|||||||
When in doubt about a signature, ask the stock wheel rather than guessing:
|
When in doubt about a signature, ask the stock wheel rather than guessing:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ../app && .venv/bin/python -c "from OCP.BRep import BRep_Tool; print(BRep_Tool.Triangulation_s.__doc__)"
|
cd ../app && uv run --project backend python -c \
|
||||||
|
"from OCP.BRep import BRep_Tool; print(BRep_Tool.Triangulation_s.__doc__)"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And after writing a module, run `make sigdiff`, which asks it about every class
|
||||||
|
at once. This is not pedantry about matching upstream — it catches the one
|
||||||
|
mistake in this codebase that is both easy to make and silent:
|
||||||
|
|
||||||
|
> `nb::init<TopoDS_Shape, gp_Vec, bool, bool, bool>` for `BRepPrimAPI_MakePrism`
|
||||||
|
> compiled fine and bound the **wrong constructor**. OCCT's finite-prism
|
||||||
|
> overload takes four arguments; the five-argument one takes a `gp_Dir` for a
|
||||||
|
> semi-infinite prism, and `gp_Dir` converts implicitly from `gp_Vec`. The
|
||||||
|
> result was a valid solid of the wrong shape, with the `Copy` and `Canonize`
|
||||||
|
> flags shifted one position along.
|
||||||
|
|
||||||
|
Anything `sigdiff` reports is either that bug or a deliberate deviation; if it
|
||||||
|
is deliberate, say so in a comment where the class is bound.
|
||||||
|
|
||||||
## 4. New toolkits
|
## 4. New toolkits
|
||||||
|
|
||||||
If the linker cannot find a symbol, the class lives in a toolkit not yet listed
|
If the linker cannot find a symbol, the class lives in a toolkit not yet listed
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ name = "n3xd-ocp"
|
|||||||
# .devN suffix: the registry never allows republishing a version, and its
|
# .devN suffix: the registry never allows republishing a version, and its
|
||||||
# cleanup rule collects only the dev ones. CMake asserts the prefix matches
|
# cleanup rule collects only the dev ones. CMake asserts the prefix matches
|
||||||
# the OCCT it found.
|
# the OCCT it found.
|
||||||
version = "7.9.3.1.dev1"
|
version = "7.9.3.1.dev2"
|
||||||
description = "nanobind bindings for the OpenCASCADE geometry kernel (drop-in OCP)"
|
description = "nanobind bindings for the OpenCASCADE geometry kernel (drop-in OCP)"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
44
src/core.cpp
44
src/core.cpp
@@ -31,6 +31,27 @@ void register_TopExp(nb::module_ &);
|
|||||||
void register_Poly(nb::module_ &);
|
void register_Poly(nb::module_ &);
|
||||||
void register_BRep(nb::module_ &);
|
void register_BRep(nb::module_ &);
|
||||||
void register_BinTools(nb::module_ &);
|
void register_BinTools(nb::module_ &);
|
||||||
|
// Inc 1 — core modeling
|
||||||
|
void register_GeomAbs(nb::module_ &);
|
||||||
|
void register_TCollection(nb::module_ &);
|
||||||
|
void register_TColgp(nb::module_ &);
|
||||||
|
void register_TColStd(nb::module_ &);
|
||||||
|
void register_GProp(nb::module_ &);
|
||||||
|
void register_BRepGProp(nb::module_ &);
|
||||||
|
void register_Bnd(nb::module_ &);
|
||||||
|
void register_BRepBndLib(nb::module_ &);
|
||||||
|
void register_Geom2d(nb::module_ &);
|
||||||
|
void register_Geom(nb::module_ &);
|
||||||
|
void register_Adaptor3d(nb::module_ &);
|
||||||
|
void register_BRepAdaptor(nb::module_ &);
|
||||||
|
void register_GeomLProp(nb::module_ &);
|
||||||
|
void register_GeomAPI(nb::module_ &);
|
||||||
|
void register_BRepBuilderAPI(nb::module_ &);
|
||||||
|
void register_BOPAlgo(nb::module_ &);
|
||||||
|
void register_BRepAlgoAPI(nb::module_ &);
|
||||||
|
void register_BRepPrimAPI(nb::module_ &);
|
||||||
|
void register_GC(nb::module_ &);
|
||||||
|
void register_BRepMesh(nb::module_ &);
|
||||||
void register_ext(nb::module_ &);
|
void register_ext(nb::module_ &);
|
||||||
|
|
||||||
NB_MODULE(_OCP, m) {
|
NB_MODULE(_OCP, m) {
|
||||||
@@ -51,6 +72,29 @@ NB_MODULE(_OCP, m) {
|
|||||||
register_BRep(m);
|
register_BRep(m);
|
||||||
register_BinTools(m);
|
register_BinTools(m);
|
||||||
|
|
||||||
|
// Inc 1. Order follows the class hierarchy: a declared base must be
|
||||||
|
// registered before anything that derives from it.
|
||||||
|
register_GeomAbs(m);
|
||||||
|
register_TCollection(m);
|
||||||
|
register_TColgp(m);
|
||||||
|
register_TColStd(m);
|
||||||
|
register_GProp(m);
|
||||||
|
register_BRepGProp(m);
|
||||||
|
register_Bnd(m);
|
||||||
|
register_BRepBndLib(m);
|
||||||
|
register_Geom2d(m);
|
||||||
|
register_Geom(m);
|
||||||
|
register_Adaptor3d(m); // before BRepAdaptor: it declares the bases
|
||||||
|
register_BRepAdaptor(m);
|
||||||
|
register_GeomLProp(m);
|
||||||
|
register_GeomAPI(m);
|
||||||
|
register_BRepBuilderAPI(m); // declares the MakeShape base the rest use
|
||||||
|
register_BOPAlgo(m);
|
||||||
|
register_BRepAlgoAPI(m);
|
||||||
|
register_BRepPrimAPI(m);
|
||||||
|
register_GC(m);
|
||||||
|
register_BRepMesh(m);
|
||||||
|
|
||||||
register_ext(m);
|
register_ext(m);
|
||||||
|
|
||||||
m.attr("__all_modules__") = ocp_module_registry();
|
m.attr("__all_modules__") = ocp_module_registry();
|
||||||
|
|||||||
69
src/modules/mod_Adaptor3d.cpp
Normal file
69
src/modules/mod_Adaptor3d.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
OCP.Adaptor3d — the abstract curve/surface interface the app actually
|
||||||
|
programs against.
|
||||||
|
|
||||||
|
Not imported by the app, but it is where every method it calls on a
|
||||||
|
BRepAdaptor_Curve/Surface is *declared*: GetType, the parameter bounds,
|
||||||
|
Value/D1 and the quadric accessors are all virtuals here. Binding them
|
||||||
|
once on the bases keeps mod_BRepAdaptor.cpp down to constructors, and any
|
||||||
|
later adaptor (Geom2dAdaptor, Adaptor3d_CurveOnSurface) inherits the
|
||||||
|
surface for free.
|
||||||
|
|
||||||
|
These are transients in OCCT 7.6+, so they are declared against
|
||||||
|
Standard_Transient and never get nb::init<>.
|
||||||
|
|
||||||
|
D1 keeps its out-parameters, because the app pre-allocates the gp_Pnt/gp_Vec
|
||||||
|
and reads them back (`adaptor.D1(u, v, pnt, d1u, d1v)`) — same as stock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <Adaptor3d_Curve.hxx>
|
||||||
|
#include <Adaptor3d_Surface.hxx>
|
||||||
|
#include <gp_Circ.hxx>
|
||||||
|
#include <gp_Cone.hxx>
|
||||||
|
#include <gp_Cylinder.hxx>
|
||||||
|
#include <gp_Elips.hxx>
|
||||||
|
#include <gp_Lin.hxx>
|
||||||
|
#include <gp_Pln.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Sphere.hxx>
|
||||||
|
#include <gp_Vec.hxx>
|
||||||
|
|
||||||
|
void register_Adaptor3d(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "Adaptor3d");
|
||||||
|
|
||||||
|
nb::class_<Adaptor3d_Curve, Standard_Transient>(m, "Adaptor3d_Curve")
|
||||||
|
.def("GetType", &Adaptor3d_Curve::GetType)
|
||||||
|
.def("FirstParameter", &Adaptor3d_Curve::FirstParameter)
|
||||||
|
.def("LastParameter", &Adaptor3d_Curve::LastParameter)
|
||||||
|
.def("Value", &Adaptor3d_Curve::Value, "U"_a, OCP_RETURN_COPY)
|
||||||
|
.def("D0", &Adaptor3d_Curve::D0, "U"_a, "P"_a)
|
||||||
|
.def("D1", &Adaptor3d_Curve::D1, "U"_a, "P"_a, "V"_a)
|
||||||
|
.def("Line", &Adaptor3d_Curve::Line, OCP_RETURN_COPY)
|
||||||
|
.def("Circle", &Adaptor3d_Curve::Circle, OCP_RETURN_COPY)
|
||||||
|
.def("Ellipse", &Adaptor3d_Curve::Ellipse, OCP_RETURN_COPY)
|
||||||
|
.def("IsClosed", &Adaptor3d_Curve::IsClosed)
|
||||||
|
.def("IsPeriodic", &Adaptor3d_Curve::IsPeriodic);
|
||||||
|
|
||||||
|
nb::class_<Adaptor3d_Surface, Standard_Transient>(m, "Adaptor3d_Surface")
|
||||||
|
.def("GetType", &Adaptor3d_Surface::GetType)
|
||||||
|
.def("FirstUParameter", &Adaptor3d_Surface::FirstUParameter)
|
||||||
|
.def("LastUParameter", &Adaptor3d_Surface::LastUParameter)
|
||||||
|
.def("FirstVParameter", &Adaptor3d_Surface::FirstVParameter)
|
||||||
|
.def("LastVParameter", &Adaptor3d_Surface::LastVParameter)
|
||||||
|
.def("IsUPeriodic", &Adaptor3d_Surface::IsUPeriodic)
|
||||||
|
.def("IsVPeriodic", &Adaptor3d_Surface::IsVPeriodic)
|
||||||
|
.def("UPeriod", &Adaptor3d_Surface::UPeriod)
|
||||||
|
.def("VPeriod", &Adaptor3d_Surface::VPeriod)
|
||||||
|
.def("IsUClosed", &Adaptor3d_Surface::IsUClosed)
|
||||||
|
.def("IsVClosed", &Adaptor3d_Surface::IsVClosed)
|
||||||
|
.def("Value", &Adaptor3d_Surface::Value, "U"_a, "V"_a, OCP_RETURN_COPY)
|
||||||
|
.def("D0", &Adaptor3d_Surface::D0, "U"_a, "V"_a, "P"_a)
|
||||||
|
.def("D1", &Adaptor3d_Surface::D1, "U"_a, "V"_a, "P"_a, "D1U"_a, "D1V"_a)
|
||||||
|
.def("Plane", &Adaptor3d_Surface::Plane, OCP_RETURN_COPY)
|
||||||
|
.def("Cylinder", &Adaptor3d_Surface::Cylinder, OCP_RETURN_COPY)
|
||||||
|
.def("Cone", &Adaptor3d_Surface::Cone, OCP_RETURN_COPY)
|
||||||
|
.def("Sphere", &Adaptor3d_Surface::Sphere, OCP_RETURN_COPY);
|
||||||
|
}
|
||||||
22
src/modules/mod_BOPAlgo.cpp
Normal file
22
src/modules/mod_BOPAlgo.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
OCP.BOPAlgo — boolean-operation options.
|
||||||
|
|
||||||
|
Only the glue enum: the app's pattern fuse sets BOPAlgo_GlueShift when it
|
||||||
|
knows the operands cannot intersect, which lets OCCT skip the intersection
|
||||||
|
stage entirely. It has to land with BRepAlgoAPI rather than later, since
|
||||||
|
SetGlue takes this type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <BOPAlgo_GlueEnum.hxx>
|
||||||
|
|
||||||
|
void register_BOPAlgo(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BOPAlgo");
|
||||||
|
|
||||||
|
nb::enum_<BOPAlgo_GlueEnum>(m, "BOPAlgo_GlueEnum", nb::is_arithmetic())
|
||||||
|
.value("BOPAlgo_GlueOff", BOPAlgo_GlueOff)
|
||||||
|
.value("BOPAlgo_GlueShift", BOPAlgo_GlueShift)
|
||||||
|
.value("BOPAlgo_GlueFull", BOPAlgo_GlueFull)
|
||||||
|
.export_values();
|
||||||
|
}
|
||||||
@@ -1,16 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
OCP.BRep — builder plus the Inc 0 half of BRep_Tool.
|
OCP.BRep — the shape builder and BRep_Tool's query statics.
|
||||||
|
|
||||||
BRep_Tool::Surface / Curve return Geom handles and land with Inc 1, where
|
Surface/Curve hand back the face's or edge's *live* Geom handle when the
|
||||||
the Geom classes arrive; Triangulation is here because it is what the
|
location is identity, and a transformed copy otherwise. The handle caster
|
||||||
tessellation path reads and what the handle spike exercises.
|
wraps whichever it is without copying, so mutating the result mutates the
|
||||||
|
model — upstream behaves the same way, and the app only ever reads.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../common/occt_module.h"
|
#include "../common/occt_module.h"
|
||||||
#include "../common/occt_policies.h"
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <nanobind/stl/tuple.h>
|
||||||
|
|
||||||
#include <BRep_Builder.hxx>
|
#include <BRep_Builder.hxx>
|
||||||
#include <BRep_Tool.hxx>
|
#include <BRep_Tool.hxx>
|
||||||
|
#include <Geom2d_Curve.hxx>
|
||||||
|
#include <Geom_Curve.hxx>
|
||||||
|
#include <Geom_Surface.hxx>
|
||||||
#include <Poly_Triangulation.hxx>
|
#include <Poly_Triangulation.hxx>
|
||||||
#include <TopLoc_Location.hxx>
|
#include <TopLoc_Location.hxx>
|
||||||
#include <TopoDS_Compound.hxx>
|
#include <TopoDS_Compound.hxx>
|
||||||
@@ -70,4 +76,34 @@ void register_BRep(nb::module_ &root) {
|
|||||||
OCP_DEF_S(
|
OCP_DEF_S(
|
||||||
cls, "IsClosed",
|
cls, "IsClosed",
|
||||||
[](const TopoDS_Shape &S) { return BRep_Tool::IsClosed(S); }, "S"_a);
|
[](const TopoDS_Shape &S) { return BRep_Tool::IsClosed(S); }, "S"_a);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "Surface",
|
||||||
|
[](const TopoDS_Face &F) { return BRep_Tool::Surface(F); }, "F"_a);
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "Surface",
|
||||||
|
[](const TopoDS_Face &F, TopLoc_Location &L) {
|
||||||
|
return BRep_Tool::Surface(F, L);
|
||||||
|
},
|
||||||
|
"F"_a, "L"_a);
|
||||||
|
|
||||||
|
// Curve reports the parameter range through two scalar out-parameters;
|
||||||
|
// upstream returns them alongside the handle, so a caller writes
|
||||||
|
// `curve, first, last = BRep_Tool.Curve_s(edge)`.
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "Curve",
|
||||||
|
[](const TopoDS_Edge &E) {
|
||||||
|
Standard_Real first = 0.0, last = 0.0;
|
||||||
|
auto curve = BRep_Tool::Curve(E, first, last);
|
||||||
|
return std::make_tuple(curve, first, last);
|
||||||
|
},
|
||||||
|
"E"_a);
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "CurveOnSurface",
|
||||||
|
[](const TopoDS_Edge &E, const TopoDS_Face &F) {
|
||||||
|
Standard_Real first = 0.0, last = 0.0;
|
||||||
|
auto curve = BRep_Tool::CurveOnSurface(E, F, first, last);
|
||||||
|
return std::make_tuple(curve, first, last);
|
||||||
|
},
|
||||||
|
"E"_a, "F"_a);
|
||||||
}
|
}
|
||||||
|
|||||||
42
src/modules/mod_BRepAdaptor.cpp
Normal file
42
src/modules/mod_BRepAdaptor.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepAdaptor — adaptors over topology.
|
||||||
|
|
||||||
|
Constructors only: every method the app calls is a virtual declared on
|
||||||
|
Adaptor3d_Curve/Adaptor3d_Surface and bound there. These are the app's
|
||||||
|
single most-used query path — "what kind of surface is this face, and what
|
||||||
|
are its axis and radius" — behind reference inference, anchor scoring and
|
||||||
|
hole recognition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_transient.h"
|
||||||
|
|
||||||
|
#include <Adaptor3d_Curve.hxx>
|
||||||
|
#include <Adaptor3d_Surface.hxx>
|
||||||
|
#include <BRepAdaptor_Curve.hxx>
|
||||||
|
#include <BRepAdaptor_Surface.hxx>
|
||||||
|
#include <TopoDS_Edge.hxx>
|
||||||
|
#include <TopoDS_Face.hxx>
|
||||||
|
|
||||||
|
void register_BRepAdaptor(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepAdaptor");
|
||||||
|
|
||||||
|
nb::class_<BRepAdaptor_Curve, Adaptor3d_Curve>(m, "BRepAdaptor_Curve")
|
||||||
|
.def(ocp_new<BRepAdaptor_Curve>())
|
||||||
|
.def(ocp_new<BRepAdaptor_Curve, const TopoDS_Edge &>(), "E"_a)
|
||||||
|
.def(ocp_new<BRepAdaptor_Curve, const TopoDS_Edge &, const TopoDS_Face &>(),
|
||||||
|
"E"_a, "F"_a)
|
||||||
|
.def("Initialize",
|
||||||
|
nb::overload_cast<const TopoDS_Edge &>(&BRepAdaptor_Curve::Initialize),
|
||||||
|
"E"_a)
|
||||||
|
.def("Edge", &BRepAdaptor_Curve::Edge, nb::rv_policy::copy);
|
||||||
|
|
||||||
|
nb::class_<BRepAdaptor_Surface, Adaptor3d_Surface>(m, "BRepAdaptor_Surface")
|
||||||
|
.def(ocp_new<BRepAdaptor_Surface>())
|
||||||
|
.def(ocp_new<BRepAdaptor_Surface, const TopoDS_Face &,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"F"_a, "R"_a = Standard_True)
|
||||||
|
.def("Initialize", &BRepAdaptor_Surface::Initialize, "F"_a,
|
||||||
|
"Restriction"_a = Standard_True)
|
||||||
|
.def("Face", &BRepAdaptor_Surface::Face, nb::rv_policy::copy);
|
||||||
|
}
|
||||||
117
src/modules/mod_BRepAlgoAPI.cpp
Normal file
117
src/modules/mod_BRepAlgoAPI.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepAlgoAPI — booleans and the splitter.
|
||||||
|
|
||||||
|
This is the one place where the binding deliberately refuses part of
|
||||||
|
upstream's surface: the two-argument constructors, which execute the
|
||||||
|
operation during construction. Every app call site also called Build(),
|
||||||
|
so the boolean ran twice and the first, destructive pass could mutate
|
||||||
|
inputs shared with the shape cache. Only the default constructor is bound;
|
||||||
|
operands go in through SetArguments/SetTools and the caller owns Build().
|
||||||
|
|
||||||
|
Section is the exception in form only: its operands arrive through
|
||||||
|
Init1/Init2 (Init2 takes a gp_Pln and builds the infinite plane face
|
||||||
|
itself), which are plain setters, so both are bound.
|
||||||
|
|
||||||
|
BRepAlgoAPI_Algo inherits BOPAlgo_Options *protected* and re-exports the
|
||||||
|
option setters with using-declarations, so there is no public upcast to
|
||||||
|
bind against — those go through lambdas on BuilderAlgo.
|
||||||
|
|
||||||
|
Build/Shape/Generated/Modified/IsDeleted all come from
|
||||||
|
BRepBuilderAPI_MakeShape. The history is the topological-naming substrate:
|
||||||
|
cad/topology/provenance.py reads it to decide which feature owns which
|
||||||
|
face, so it must be exact, and un-tracked sub-shapes must raise a catchable
|
||||||
|
Python exception rather than abort (the app wraps those calls in
|
||||||
|
try/except).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <BOPAlgo_GlueEnum.hxx>
|
||||||
|
#include <BRepAlgoAPI_BooleanOperation.hxx>
|
||||||
|
#include <BRepAlgoAPI_BuilderAlgo.hxx>
|
||||||
|
#include <BRepAlgoAPI_Common.hxx>
|
||||||
|
#include <BRepAlgoAPI_Cut.hxx>
|
||||||
|
#include <BRepAlgoAPI_Fuse.hxx>
|
||||||
|
#include <BRepAlgoAPI_Section.hxx>
|
||||||
|
#include <BRepAlgoAPI_Splitter.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakeShape.hxx>
|
||||||
|
#include <TopTools_ListOfShape.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
#include <gp_Pln.hxx>
|
||||||
|
|
||||||
|
void register_BRepAlgoAPI(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepAlgoAPI");
|
||||||
|
|
||||||
|
// BRepAlgoAPI_Algo is skipped as a declared base: it adds nothing public
|
||||||
|
// of its own, and nanobind only needs an unambiguous upcast path.
|
||||||
|
nb::class_<BRepAlgoAPI_BuilderAlgo, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepAlgoAPI_BuilderAlgo")
|
||||||
|
.def("SetArguments", &BRepAlgoAPI_BuilderAlgo::SetArguments, "theLS"_a)
|
||||||
|
.def("Arguments", &BRepAlgoAPI_BuilderAlgo::Arguments, OCP_RETURN_COPY)
|
||||||
|
.def("SetNonDestructive", &BRepAlgoAPI_BuilderAlgo::SetNonDestructive,
|
||||||
|
"theFlag"_a)
|
||||||
|
.def("SetGlue", &BRepAlgoAPI_BuilderAlgo::SetGlue, "theGlue"_a)
|
||||||
|
.def("HasModified", &BRepAlgoAPI_BuilderAlgo::HasModified)
|
||||||
|
.def("HasGenerated", &BRepAlgoAPI_BuilderAlgo::HasGenerated)
|
||||||
|
.def("HasDeleted", &BRepAlgoAPI_BuilderAlgo::HasDeleted)
|
||||||
|
// Inherited from the protected BOPAlgo_Options base through
|
||||||
|
// using-declarations, so they are reachable by name but not by
|
||||||
|
// member pointer.
|
||||||
|
.def(
|
||||||
|
"SetFuzzyValue",
|
||||||
|
[](BRepAlgoAPI_BuilderAlgo &self, Standard_Real v) {
|
||||||
|
self.SetFuzzyValue(v);
|
||||||
|
},
|
||||||
|
"theFuzz"_a)
|
||||||
|
.def(
|
||||||
|
"SetRunParallel",
|
||||||
|
[](BRepAlgoAPI_BuilderAlgo &self, Standard_Boolean f) {
|
||||||
|
self.SetRunParallel(f);
|
||||||
|
},
|
||||||
|
"theFlag"_a)
|
||||||
|
.def(
|
||||||
|
"SetUseOBB",
|
||||||
|
[](BRepAlgoAPI_BuilderAlgo &self, Standard_Boolean f) {
|
||||||
|
self.SetUseOBB(f);
|
||||||
|
},
|
||||||
|
"theUseOBB"_a);
|
||||||
|
|
||||||
|
nb::class_<BRepAlgoAPI_BooleanOperation, BRepAlgoAPI_BuilderAlgo>(
|
||||||
|
m, "BRepAlgoAPI_BooleanOperation")
|
||||||
|
.def("SetTools", &BRepAlgoAPI_BooleanOperation::SetTools, "theLS"_a)
|
||||||
|
.def("Tools", &BRepAlgoAPI_BooleanOperation::Tools, OCP_RETURN_COPY)
|
||||||
|
.def("Shape1", &BRepAlgoAPI_BooleanOperation::Shape1, OCP_RETURN_COPY)
|
||||||
|
.def("Shape2", &BRepAlgoAPI_BooleanOperation::Shape2, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepAlgoAPI_Fuse, BRepAlgoAPI_BooleanOperation>(
|
||||||
|
m, "BRepAlgoAPI_Fuse")
|
||||||
|
.def(nb::init<>());
|
||||||
|
nb::class_<BRepAlgoAPI_Cut, BRepAlgoAPI_BooleanOperation>(m,
|
||||||
|
"BRepAlgoAPI_Cut")
|
||||||
|
.def(nb::init<>());
|
||||||
|
nb::class_<BRepAlgoAPI_Common, BRepAlgoAPI_BooleanOperation>(
|
||||||
|
m, "BRepAlgoAPI_Common")
|
||||||
|
.def(nb::init<>());
|
||||||
|
|
||||||
|
nb::class_<BRepAlgoAPI_Section, BRepAlgoAPI_BooleanOperation>(
|
||||||
|
m, "BRepAlgoAPI_Section")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("Init1",
|
||||||
|
nb::overload_cast<const TopoDS_Shape &>(&BRepAlgoAPI_Section::Init1),
|
||||||
|
"S1"_a)
|
||||||
|
.def("Init2",
|
||||||
|
nb::overload_cast<const TopoDS_Shape &>(&BRepAlgoAPI_Section::Init2),
|
||||||
|
"S2"_a)
|
||||||
|
.def("Init2", nb::overload_cast<const gp_Pln &>(&BRepAlgoAPI_Section::Init2),
|
||||||
|
"Pl"_a)
|
||||||
|
.def("Approximation", &BRepAlgoAPI_Section::Approximation, "B"_a)
|
||||||
|
.def("ComputePCurveOn1", &BRepAlgoAPI_Section::ComputePCurveOn1, "B"_a)
|
||||||
|
.def("ComputePCurveOn2", &BRepAlgoAPI_Section::ComputePCurveOn2, "B"_a);
|
||||||
|
|
||||||
|
nb::class_<BRepAlgoAPI_Splitter, BRepAlgoAPI_BuilderAlgo>(
|
||||||
|
m, "BRepAlgoAPI_Splitter")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("SetTools", &BRepAlgoAPI_Splitter::SetTools, "theLS"_a)
|
||||||
|
.def("Tools", &BRepAlgoAPI_Splitter::Tools, OCP_RETURN_COPY);
|
||||||
|
}
|
||||||
36
src/modules/mod_BRepBndLib.cpp
Normal file
36
src/modules/mod_BRepBndLib.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepBndLib — bounding box of a shape.
|
||||||
|
|
||||||
|
Only Add is used. ``useTriangulation`` matters to callers: with an
|
||||||
|
existing mesh it is both faster and tighter than the default, which
|
||||||
|
enlarges the box by the surfaces' own tolerance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <BRepBndLib.hxx>
|
||||||
|
#include <Bnd_Box.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
|
||||||
|
void register_BRepBndLib(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepBndLib");
|
||||||
|
|
||||||
|
nb::class_<BRepBndLib> cls(m, "BRepBndLib");
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "Add",
|
||||||
|
[](const TopoDS_Shape &S, Bnd_Box &B, Standard_Boolean useTriangulation) {
|
||||||
|
BRepBndLib::Add(S, B, useTriangulation);
|
||||||
|
},
|
||||||
|
"S"_a, "B"_a, "useTriangulation"_a = Standard_True, OCP_NOGIL);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "AddOptimal",
|
||||||
|
[](const TopoDS_Shape &S, Bnd_Box &B, Standard_Boolean useTriangulation,
|
||||||
|
Standard_Boolean useShapeTolerance) {
|
||||||
|
BRepBndLib::AddOptimal(S, B, useTriangulation, useShapeTolerance);
|
||||||
|
},
|
||||||
|
"S"_a, "B"_a, "useTriangulation"_a = Standard_True,
|
||||||
|
"useShapeTolerance"_a = Standard_False, OCP_NOGIL);
|
||||||
|
}
|
||||||
164
src/modules/mod_BRepBuilderAPI.cpp
Normal file
164
src/modules/mod_BRepBuilderAPI.cpp
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepBuilderAPI — topology construction, and the base class every other
|
||||||
|
maker in this binding inherits.
|
||||||
|
|
||||||
|
BRepBuilderAPI_MakeShape is where Build/Shape/Generated/Modified/IsDeleted
|
||||||
|
are declared, so binding them once here covers the booleans, the primitive
|
||||||
|
makers, the fillet/chamfer builders and the splitter through ordinary
|
||||||
|
virtual dispatch. That matters beyond tidiness: the app's provenance layer
|
||||||
|
(cad/topology/provenance.py) is deliberately duck-typed over "anything with
|
||||||
|
a history", and this is what makes every such builder answer it.
|
||||||
|
|
||||||
|
Generated/Modified return the builder's own list by reference in C++; they
|
||||||
|
are copied out here, so the Python list stays valid after the builder dies
|
||||||
|
and its elements follow the by-value sub-shape rule.
|
||||||
|
|
||||||
|
Build() takes a Message_ProgressRange the app never supplies; it is bound
|
||||||
|
argument-less, per the convention in adding-symbols.md.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
#include "../common/occt_transient.h"
|
||||||
|
|
||||||
|
#include <BRepBuilderAPI_Command.hxx>
|
||||||
|
#include <BRepBuilderAPI_Copy.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakePolygon.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakeShape.hxx>
|
||||||
|
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||||
|
#include <BRepBuilderAPI_Sewing.hxx>
|
||||||
|
#include <BRepBuilderAPI_Transform.hxx>
|
||||||
|
#include <Geom2d_Curve.hxx>
|
||||||
|
#include <gp_Circ.hxx>
|
||||||
|
#include <gp_Lin.hxx>
|
||||||
|
#include <gp_Pln.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Trsf.hxx>
|
||||||
|
#include <Geom_Curve.hxx>
|
||||||
|
#include <Geom_Surface.hxx>
|
||||||
|
#include <TopTools_ListOfShape.hxx>
|
||||||
|
#include <TopoDS_Edge.hxx>
|
||||||
|
#include <TopoDS_Face.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
#include <TopoDS_Wire.hxx>
|
||||||
|
|
||||||
|
void register_BRepBuilderAPI(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepBuilderAPI");
|
||||||
|
|
||||||
|
// Abstract bases: registered for the shared surface, never constructed.
|
||||||
|
nb::class_<BRepBuilderAPI_Command>(m, "BRepBuilderAPI_Command")
|
||||||
|
.def("IsDone", &BRepBuilderAPI_Command::IsDone);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_MakeShape, BRepBuilderAPI_Command>(
|
||||||
|
m, "BRepBuilderAPI_MakeShape")
|
||||||
|
.def(
|
||||||
|
"Build",
|
||||||
|
[](BRepBuilderAPI_MakeShape &self) { self.Build(); }, OCP_NOGIL)
|
||||||
|
.def("Shape", &BRepBuilderAPI_MakeShape::Shape, OCP_RETURN_COPY)
|
||||||
|
.def("Generated", &BRepBuilderAPI_MakeShape::Generated, "S"_a,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("Modified", &BRepBuilderAPI_MakeShape::Modified, "S"_a,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("IsDeleted", &BRepBuilderAPI_MakeShape::IsDeleted, "S"_a);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ makers --
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_MakeEdge")
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &>(), "P1"_a, "P2"_a)
|
||||||
|
.def(nb::init<const gp_Circ &>(), "L"_a)
|
||||||
|
.def(nb::init<const gp_Lin &>(), "L"_a)
|
||||||
|
.def(nb::init<const opencascade::handle<Geom_Curve> &>(), "L"_a)
|
||||||
|
.def(nb::init<const opencascade::handle<Geom2d_Curve> &,
|
||||||
|
const opencascade::handle<Geom_Surface> &>(),
|
||||||
|
"L"_a, "S"_a)
|
||||||
|
.def(nb::init<const opencascade::handle<Geom2d_Curve> &,
|
||||||
|
const opencascade::handle<Geom_Surface> &,
|
||||||
|
const Standard_Real, const Standard_Real>(),
|
||||||
|
"L"_a, "S"_a, "p1"_a, "p2"_a)
|
||||||
|
.def("Edge", &BRepBuilderAPI_MakeEdge::Edge, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_MakeWire")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const TopoDS_Edge &>(), "E"_a)
|
||||||
|
.def(nb::init<const TopoDS_Wire &>(), "W"_a)
|
||||||
|
.def("Add", nb::overload_cast<const TopoDS_Edge &>(
|
||||||
|
&BRepBuilderAPI_MakeWire::Add),
|
||||||
|
"E"_a)
|
||||||
|
.def("Add", nb::overload_cast<const TopoDS_Wire &>(
|
||||||
|
&BRepBuilderAPI_MakeWire::Add),
|
||||||
|
"W"_a)
|
||||||
|
.def("Add", nb::overload_cast<const TopTools_ListOfShape &>(
|
||||||
|
&BRepBuilderAPI_MakeWire::Add),
|
||||||
|
"L"_a)
|
||||||
|
.def("Wire", &BRepBuilderAPI_MakeWire::Wire, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_MakeFace")
|
||||||
|
.def(nb::init<const TopoDS_Face &>(), "F"_a)
|
||||||
|
.def(nb::init<const gp_Pln &>(), "P"_a)
|
||||||
|
.def(nb::init<const gp_Pln &, const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real, const Standard_Real>(),
|
||||||
|
"P"_a, "UMin"_a, "UMax"_a, "VMin"_a, "VMax"_a)
|
||||||
|
.def(nb::init<const TopoDS_Wire &, const Standard_Boolean>(), "W"_a,
|
||||||
|
"OnlyPlane"_a = Standard_False)
|
||||||
|
.def(nb::init<const opencascade::handle<Geom_Surface> &,
|
||||||
|
const TopoDS_Wire &, const Standard_Boolean>(),
|
||||||
|
"S"_a, "W"_a, "Inside"_a = Standard_True)
|
||||||
|
.def("Add", &BRepBuilderAPI_MakeFace::Add, "W"_a)
|
||||||
|
.def("Face", &BRepBuilderAPI_MakeFace::Face, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_MakePolygon, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_MakePolygon")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &>(), "P1"_a, "P2"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &, const gp_Pnt &,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"P1"_a, "P2"_a, "P3"_a, "Close"_a = Standard_False)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &, const gp_Pnt &,
|
||||||
|
const gp_Pnt &, const Standard_Boolean>(),
|
||||||
|
"P1"_a, "P2"_a, "P3"_a, "P4"_a, "Close"_a = Standard_False)
|
||||||
|
.def("Add", nb::overload_cast<const gp_Pnt &>(
|
||||||
|
&BRepBuilderAPI_MakePolygon::Add),
|
||||||
|
"P"_a)
|
||||||
|
.def("Added", &BRepBuilderAPI_MakePolygon::Added)
|
||||||
|
.def("Close", &BRepBuilderAPI_MakePolygon::Close)
|
||||||
|
.def("Wire", &BRepBuilderAPI_MakePolygon::Wire, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_Transform, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_Transform")
|
||||||
|
.def(nb::init<const gp_Trsf &>(), "T"_a)
|
||||||
|
.def(nb::init<const TopoDS_Shape &, const gp_Trsf &,
|
||||||
|
const Standard_Boolean, const Standard_Boolean>(),
|
||||||
|
"theShape"_a, "theTrsf"_a, "theCopyGeom"_a = Standard_False,
|
||||||
|
"theCopyMesh"_a = Standard_False, OCP_NOGIL)
|
||||||
|
.def("ModifiedShape", &BRepBuilderAPI_Transform::ModifiedShape, "S"_a,
|
||||||
|
OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepBuilderAPI_Copy, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepBuilderAPI_Copy")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const TopoDS_Shape &, const Standard_Boolean,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"S"_a, "copyGeom"_a = Standard_True,
|
||||||
|
"copyMesh"_a = Standard_False, OCP_NOGIL);
|
||||||
|
|
||||||
|
// Sewing is a transient and not a MakeShape — it has its own result
|
||||||
|
// accessor rather than Shape().
|
||||||
|
nb::class_<BRepBuilderAPI_Sewing, Standard_Transient>(
|
||||||
|
m, "BRepBuilderAPI_Sewing")
|
||||||
|
.def(ocp_new<BRepBuilderAPI_Sewing, const Standard_Real,
|
||||||
|
const Standard_Boolean, const Standard_Boolean,
|
||||||
|
const Standard_Boolean, const Standard_Boolean>(),
|
||||||
|
"tolerance"_a = 1.0e-06, "option1"_a = Standard_True,
|
||||||
|
"option2"_a = Standard_True, "option3"_a = Standard_True,
|
||||||
|
"option4"_a = Standard_False)
|
||||||
|
.def("Add", &BRepBuilderAPI_Sewing::Add, "shape"_a)
|
||||||
|
.def(
|
||||||
|
"Perform", [](BRepBuilderAPI_Sewing &self) { self.Perform(); },
|
||||||
|
OCP_NOGIL)
|
||||||
|
.def("SewedShape", &BRepBuilderAPI_Sewing::SewedShape, OCP_RETURN_COPY);
|
||||||
|
}
|
||||||
73
src/modules/mod_BRepGProp.cpp
Normal file
73
src/modules/mod_BRepGProp.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepGProp — mass properties of a shape.
|
||||||
|
|
||||||
|
Three statics, all filling a GProp_GProps: LinearProperties (edge length),
|
||||||
|
SurfaceProperties (area) and VolumeProperties. Both eps overloads are
|
||||||
|
bound: the default quadrature is inaccurate on spline-bounded faces, and
|
||||||
|
the app's area readouts may move onto the eps form (see the app's
|
||||||
|
NOTEPAD). A test already depends on the 3-argument form.
|
||||||
|
|
||||||
|
SurfaceProperties is the single hottest kernel call in a rebuild — 94 % of
|
||||||
|
face_candidate_anchors — so the GIL is released here and n3xd_ocp.measure
|
||||||
|
offers the batched form.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <BRepGProp.hxx>
|
||||||
|
#include <GProp_GProps.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
|
||||||
|
void register_BRepGProp(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepGProp");
|
||||||
|
|
||||||
|
nb::class_<BRepGProp> cls(m, "BRepGProp");
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "LinearProperties",
|
||||||
|
[](const TopoDS_Shape &S, GProp_GProps &LProps, Standard_Boolean SkipShared,
|
||||||
|
Standard_Boolean UseTriangulation) {
|
||||||
|
BRepGProp::LinearProperties(S, LProps, SkipShared, UseTriangulation);
|
||||||
|
},
|
||||||
|
"S"_a, "LProps"_a, "SkipShared"_a = Standard_False,
|
||||||
|
"UseTriangulation"_a = Standard_False, OCP_NOGIL);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "SurfaceProperties",
|
||||||
|
[](const TopoDS_Shape &S, GProp_GProps &SProps, Standard_Boolean SkipShared,
|
||||||
|
Standard_Boolean UseTriangulation) {
|
||||||
|
BRepGProp::SurfaceProperties(S, SProps, SkipShared, UseTriangulation);
|
||||||
|
},
|
||||||
|
"S"_a, "SProps"_a, "SkipShared"_a = Standard_False,
|
||||||
|
"UseTriangulation"_a = Standard_False, OCP_NOGIL);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "SurfaceProperties",
|
||||||
|
[](const TopoDS_Shape &S, GProp_GProps &SProps, Standard_Real Eps,
|
||||||
|
Standard_Boolean SkipShared) {
|
||||||
|
return BRepGProp::SurfaceProperties(S, SProps, Eps, SkipShared);
|
||||||
|
},
|
||||||
|
"S"_a, "SProps"_a, "Eps"_a, "SkipShared"_a = Standard_False, OCP_NOGIL);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "VolumeProperties",
|
||||||
|
[](const TopoDS_Shape &S, GProp_GProps &VProps, Standard_Boolean OnlyClosed,
|
||||||
|
Standard_Boolean SkipShared, Standard_Boolean UseTriangulation) {
|
||||||
|
BRepGProp::VolumeProperties(S, VProps, OnlyClosed, SkipShared,
|
||||||
|
UseTriangulation);
|
||||||
|
},
|
||||||
|
"S"_a, "VProps"_a, "OnlyClosed"_a = Standard_False,
|
||||||
|
"SkipShared"_a = Standard_False, "UseTriangulation"_a = Standard_False,
|
||||||
|
OCP_NOGIL);
|
||||||
|
|
||||||
|
OCP_DEF_S(
|
||||||
|
cls, "VolumeProperties",
|
||||||
|
[](const TopoDS_Shape &S, GProp_GProps &VProps, Standard_Real Eps,
|
||||||
|
Standard_Boolean OnlyClosed, Standard_Boolean SkipShared) {
|
||||||
|
return BRepGProp::VolumeProperties(S, VProps, Eps, OnlyClosed,
|
||||||
|
SkipShared);
|
||||||
|
},
|
||||||
|
"S"_a, "VProps"_a, "Eps"_a, "OnlyClosed"_a = Standard_False,
|
||||||
|
"SkipShared"_a = Standard_False, OCP_NOGIL);
|
||||||
|
}
|
||||||
35
src/modules/mod_BRepMesh.cpp
Normal file
35
src/modules/mod_BRepMesh.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepMesh — triangulation.
|
||||||
|
|
||||||
|
One class, always constructed in its executing form: meshing a shape has
|
||||||
|
no deferred API, and the result is written into the shape rather than
|
||||||
|
returned. The GIL is released for it — this is one of the longest single
|
||||||
|
kernel calls a rebuild makes, and the app already runs it with OCCT's own
|
||||||
|
parallel flag on.
|
||||||
|
|
||||||
|
In 7.9 BRepMesh_IncrementalMesh is a transient (via BRepMesh_DiscretRoot),
|
||||||
|
so its constructor goes through ocp_new.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
#include "../common/occt_transient.h"
|
||||||
|
|
||||||
|
#include <BRepMesh_IncrementalMesh.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
|
||||||
|
void register_BRepMesh(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepMesh");
|
||||||
|
|
||||||
|
nb::class_<BRepMesh_IncrementalMesh, Standard_Transient>(
|
||||||
|
m, "BRepMesh_IncrementalMesh")
|
||||||
|
.def(ocp_new<BRepMesh_IncrementalMesh>())
|
||||||
|
.def(ocp_new<BRepMesh_IncrementalMesh, const TopoDS_Shape &,
|
||||||
|
const Standard_Real, const Standard_Boolean,
|
||||||
|
const Standard_Real, const Standard_Boolean>(),
|
||||||
|
"theShape"_a, "theLinDeflection"_a,
|
||||||
|
"isRelative"_a = Standard_False, "theAngDeflection"_a = 0.5,
|
||||||
|
"isInParallel"_a = Standard_False, OCP_NOGIL)
|
||||||
|
.def("IsDone", &BRepMesh_IncrementalMesh::IsDone)
|
||||||
|
.def("IsModified", &BRepMesh_IncrementalMesh::IsModified);
|
||||||
|
}
|
||||||
110
src/modules/mod_BRepPrimAPI.cpp
Normal file
110
src/modules/mod_BRepPrimAPI.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
OCP.BRepPrimAPI — primitive solids and sweeps.
|
||||||
|
|
||||||
|
Every one of these is a BRepBuilderAPI_MakeShape, so Build/Shape and the
|
||||||
|
history come from there. MakePrism matters for history in particular: an
|
||||||
|
extrude's provenance is read off it exactly as a boolean's is.
|
||||||
|
|
||||||
|
These constructors do build their shape, and there is no deferred form to
|
||||||
|
prefer — unlike the BRepAlgoAPI booleans, whose two-argument constructors
|
||||||
|
duplicate an explicit Build(). They are bound as stock does.
|
||||||
|
|
||||||
|
MakeHalfSpace produces an infinite solid used as a clipping operand; its
|
||||||
|
result comes back through Solid(), not Shape().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <BRepBuilderAPI_MakeShape.hxx>
|
||||||
|
#include <BRepPrimAPI_MakeBox.hxx>
|
||||||
|
#include <BRepPrimAPI_MakeCone.hxx>
|
||||||
|
#include <BRepPrimAPI_MakeCylinder.hxx>
|
||||||
|
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||||
|
#include <BRepPrimAPI_MakePrism.hxx>
|
||||||
|
#include <BRepPrimAPI_MakeRevol.hxx>
|
||||||
|
#include <TopoDS_Face.hxx>
|
||||||
|
#include <TopoDS_Shell.hxx>
|
||||||
|
#include <gp_Ax1.hxx>
|
||||||
|
#include <gp_Ax2.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Vec.hxx>
|
||||||
|
#include <TopoDS_Shape.hxx>
|
||||||
|
|
||||||
|
void register_BRepPrimAPI(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "BRepPrimAPI");
|
||||||
|
|
||||||
|
nb::class_<BRepPrimAPI_MakeBox, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakeBox")
|
||||||
|
.def(nb::init<const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"dx"_a, "dy"_a, "dz"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"P"_a, "dx"_a, "dy"_a, "dz"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &>(), "P1"_a, "P2"_a)
|
||||||
|
.def(nb::init<const gp_Ax2 &, const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"Axes"_a, "dx"_a, "dy"_a, "dz"_a);
|
||||||
|
|
||||||
|
nb::class_<BRepPrimAPI_MakeCylinder, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakeCylinder")
|
||||||
|
.def(nb::init<const Standard_Real, const Standard_Real>(), "R"_a, "H"_a)
|
||||||
|
.def(nb::init<const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"R"_a, "H"_a, "Angle"_a)
|
||||||
|
.def(nb::init<const gp_Ax2 &, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"Axes"_a, "R"_a, "H"_a)
|
||||||
|
.def(nb::init<const gp_Ax2 &, const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"Axes"_a, "R"_a, "H"_a, "Angle"_a);
|
||||||
|
|
||||||
|
nb::class_<BRepPrimAPI_MakeCone, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakeCone")
|
||||||
|
.def(nb::init<const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"R1"_a, "R2"_a, "H"_a)
|
||||||
|
.def(nb::init<const gp_Ax2 &, const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"Axes"_a, "R1"_a, "R2"_a, "H"_a);
|
||||||
|
|
||||||
|
nb::class_<BRepPrimAPI_MakeHalfSpace, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakeHalfSpace")
|
||||||
|
.def(nb::init<const TopoDS_Face &, const gp_Pnt &>(), "Face"_a,
|
||||||
|
"RefPnt"_a)
|
||||||
|
.def(nb::init<const TopoDS_Shell &, const gp_Pnt &>(), "Shell"_a,
|
||||||
|
"RefPnt"_a)
|
||||||
|
.def("Solid", &BRepPrimAPI_MakeHalfSpace::Solid, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
// Only the finite (gp_Vec) form is bound. The sibling overload takes a
|
||||||
|
// gp_Dir for a semi-infinite prism, and gp_Dir converts implicitly from
|
||||||
|
// gp_Vec — so binding both invites a call meant for one to land silently
|
||||||
|
// on the other, with valid but wrong geometry. The app builds finite
|
||||||
|
// prisms only.
|
||||||
|
nb::class_<BRepPrimAPI_MakePrism, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakePrism")
|
||||||
|
.def(nb::init<const TopoDS_Shape &, const gp_Vec &,
|
||||||
|
const Standard_Boolean, const Standard_Boolean>(),
|
||||||
|
"S"_a, "V"_a, "Copy"_a = Standard_False,
|
||||||
|
"Canonize"_a = Standard_True, OCP_NOGIL)
|
||||||
|
.def("FirstShape",
|
||||||
|
nb::overload_cast<>(&BRepPrimAPI_MakePrism::FirstShape),
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("LastShape", nb::overload_cast<>(&BRepPrimAPI_MakePrism::LastShape),
|
||||||
|
OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<BRepPrimAPI_MakeRevol, BRepBuilderAPI_MakeShape>(
|
||||||
|
m, "BRepPrimAPI_MakeRevol")
|
||||||
|
.def(nb::init<const TopoDS_Shape &, const gp_Ax1 &, const Standard_Real,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"S"_a, "A"_a, "D"_a, "Copy"_a = Standard_False, OCP_NOGIL)
|
||||||
|
.def(nb::init<const TopoDS_Shape &, const gp_Ax1 &,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"S"_a, "A"_a, "Copy"_a = Standard_False, OCP_NOGIL)
|
||||||
|
.def("FirstShape",
|
||||||
|
nb::overload_cast<>(&BRepPrimAPI_MakeRevol::FirstShape),
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("LastShape", nb::overload_cast<>(&BRepPrimAPI_MakeRevol::LastShape),
|
||||||
|
OCP_RETURN_COPY);
|
||||||
|
}
|
||||||
54
src/modules/mod_Bnd.cpp
Normal file
54
src/modules/mod_Bnd.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
OCP.Bnd — axis-aligned bounding boxes.
|
||||||
|
|
||||||
|
Get() returns six scalar out-parameters as a tuple, which is how the app
|
||||||
|
unpacks it (`xmin, ymin, zmin, xmax, ymax, zmax = box.Get()`) and what
|
||||||
|
upstream OCP does. A void box raises from OCCT, so callers check IsVoid
|
||||||
|
first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <nanobind/stl/tuple.h>
|
||||||
|
|
||||||
|
#include <Bnd_Box.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Trsf.hxx>
|
||||||
|
|
||||||
|
void register_Bnd(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "Bnd");
|
||||||
|
|
||||||
|
nb::class_<Bnd_Box>(m, "Bnd_Box")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &>(), "theMin"_a, "theMax"_a)
|
||||||
|
.def("Get",
|
||||||
|
[](const Bnd_Box &self) {
|
||||||
|
Standard_Real xmin = 0.0, ymin = 0.0, zmin = 0.0;
|
||||||
|
Standard_Real xmax = 0.0, ymax = 0.0, zmax = 0.0;
|
||||||
|
self.Get(xmin, ymin, zmin, xmax, ymax, zmax);
|
||||||
|
return std::make_tuple(xmin, ymin, zmin, xmax, ymax, zmax);
|
||||||
|
})
|
||||||
|
.def("CornerMin", &Bnd_Box::CornerMin, OCP_RETURN_COPY)
|
||||||
|
.def("CornerMax", &Bnd_Box::CornerMax, OCP_RETURN_COPY)
|
||||||
|
.def("IsVoid", &Bnd_Box::IsVoid)
|
||||||
|
.def("SetVoid", &Bnd_Box::SetVoid)
|
||||||
|
.def("SetGap", &Bnd_Box::SetGap, "Tol"_a)
|
||||||
|
.def("GetGap", &Bnd_Box::GetGap)
|
||||||
|
.def("Add", nb::overload_cast<const Bnd_Box &>(&Bnd_Box::Add), "Other"_a)
|
||||||
|
.def("Add", nb::overload_cast<const gp_Pnt &>(&Bnd_Box::Add), "P"_a)
|
||||||
|
.def("Update", nb::overload_cast<const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real, const Standard_Real,
|
||||||
|
const Standard_Real, const Standard_Real>(
|
||||||
|
&Bnd_Box::Update),
|
||||||
|
"aXmin"_a, "aYmin"_a, "aZmin"_a, "aXmax"_a, "aYmax"_a, "aZmax"_a)
|
||||||
|
.def("Enlarge", &Bnd_Box::Enlarge, "Tol"_a)
|
||||||
|
.def("IsOut", nb::overload_cast<const gp_Pnt &>(&Bnd_Box::IsOut,
|
||||||
|
nb::const_),
|
||||||
|
"P"_a)
|
||||||
|
.def("IsOut", nb::overload_cast<const Bnd_Box &>(&Bnd_Box::IsOut,
|
||||||
|
nb::const_),
|
||||||
|
"Other"_a)
|
||||||
|
.def("SquareExtent", &Bnd_Box::SquareExtent)
|
||||||
|
.def("Transformed", &Bnd_Box::Transformed, "T"_a, OCP_RETURN_COPY);
|
||||||
|
}
|
||||||
25
src/modules/mod_GC.cpp
Normal file
25
src/modules/mod_GC.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
OCP.GC — 3D geometry constructors.
|
||||||
|
|
||||||
|
One use: the sketch builder turns a three-point arc into a trimmed circle
|
||||||
|
before handing it to BRepBuilderAPI_MakeEdge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <GC_MakeArcOfCircle.hxx>
|
||||||
|
#include <gp_Circ.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
|
||||||
|
void register_GC(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "GC");
|
||||||
|
|
||||||
|
nb::class_<GC_MakeArcOfCircle>(m, "GC_MakeArcOfCircle")
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Pnt &, const gp_Pnt &>(), "P1"_a,
|
||||||
|
"P2"_a, "P3"_a)
|
||||||
|
.def(nb::init<const gp_Circ &, const gp_Pnt &, const gp_Pnt &,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"Circ"_a, "P1"_a, "P2"_a, "Sense"_a)
|
||||||
|
.def("IsDone", &GC_MakeArcOfCircle::IsDone)
|
||||||
|
.def("Value", &GC_MakeArcOfCircle::Value);
|
||||||
|
}
|
||||||
57
src/modules/mod_GProp.cpp
Normal file
57
src/modules/mod_GProp.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
OCP.GProp — the result object every mass measurement writes into.
|
||||||
|
|
||||||
|
BRepGProp fills a GProp_GProps; the app reads Mass() (which is length, area
|
||||||
|
or volume depending on which BRepGProp entry point ran) and CentreOfMass().
|
||||||
|
The principal-axis block is used once, by cad/measurement.py's inertia
|
||||||
|
readout.
|
||||||
|
|
||||||
|
OCCT reports the moments and radii through scalar out-parameters; those
|
||||||
|
become tuples here, matching upstream OCP. Object out-parameters stay
|
||||||
|
out-parameters (see BRep_Tool.Triangulation_s) — the distinction is that a
|
||||||
|
caller cannot pre-allocate a Standard_Real to be written through.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <nanobind/stl/tuple.h>
|
||||||
|
|
||||||
|
#include <GProp_GProps.hxx>
|
||||||
|
#include <GProp_PrincipalProps.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Vec.hxx>
|
||||||
|
|
||||||
|
void register_GProp(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "GProp");
|
||||||
|
|
||||||
|
nb::class_<GProp_PrincipalProps>(m, "GProp_PrincipalProps")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("Moments",
|
||||||
|
[](const GProp_PrincipalProps &self) {
|
||||||
|
Standard_Real ixx = 0.0, iyy = 0.0, izz = 0.0;
|
||||||
|
self.Moments(ixx, iyy, izz);
|
||||||
|
return std::make_tuple(ixx, iyy, izz);
|
||||||
|
})
|
||||||
|
.def("RadiusOfGyration",
|
||||||
|
[](const GProp_PrincipalProps &self) {
|
||||||
|
Standard_Real rxx = 0.0, ryy = 0.0, rzz = 0.0;
|
||||||
|
self.RadiusOfGyration(rxx, ryy, rzz);
|
||||||
|
return std::make_tuple(rxx, ryy, rzz);
|
||||||
|
})
|
||||||
|
.def("FirstAxisOfInertia", &GProp_PrincipalProps::FirstAxisOfInertia,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("SecondAxisOfInertia", &GProp_PrincipalProps::SecondAxisOfInertia,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("ThirdAxisOfInertia", &GProp_PrincipalProps::ThirdAxisOfInertia,
|
||||||
|
OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<GProp_GProps>(m, "GProp_GProps")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &>(), "SLocation"_a)
|
||||||
|
.def("Mass", &GProp_GProps::Mass)
|
||||||
|
.def("CentreOfMass", &GProp_GProps::CentreOfMass, OCP_RETURN_COPY)
|
||||||
|
.def("PrincipalProperties", &GProp_GProps::PrincipalProperties,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("Add", &GProp_GProps::Add, "Item"_a, "Density"_a = 1.0);
|
||||||
|
}
|
||||||
87
src/modules/mod_Geom.cpp
Normal file
87
src/modules/mod_Geom.cpp
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
OCP.Geom — the analytic curves and surfaces behind the topology.
|
||||||
|
|
||||||
|
The app imports only three of these, but it *receives* the two abstract
|
||||||
|
bases: BRep_Tool.Surface_s hands back a Handle(Geom_Surface), and
|
||||||
|
GC_MakeArcOfCircle a Handle(Geom_TrimmedCurve), both of which then travel
|
||||||
|
into BRepBuilderAPI_MakeEdge/MakeFace. So the bases have to be registered
|
||||||
|
for those returns to convert, and the concrete leaves for the constructors
|
||||||
|
the app calls.
|
||||||
|
|
||||||
|
Deliberately partial: the concrete subclasses OCCT may hand back
|
||||||
|
(Geom_Plane, Geom_Circle, ...) are not registered, so such a return arrives
|
||||||
|
as its static type, Geom_Surface. That is fine here — the app reads
|
||||||
|
surface *kinds* off BRepAdaptor_Surface, never off the Geom object, and no
|
||||||
|
call site does DownCast or isinstance on one. Add a leaf the day one does.
|
||||||
|
|
||||||
|
Surface_s returns the face's live surface when the location is identity, a
|
||||||
|
transformed copy otherwise; the handle caster wraps whatever OCCT gives
|
||||||
|
back without copying, which is upstream's behaviour too.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
#include "../common/occt_transient.h"
|
||||||
|
|
||||||
|
#include <Geom_BSplineCurve.hxx>
|
||||||
|
#include <Geom_BSplineSurface.hxx>
|
||||||
|
#include <Geom_CylindricalSurface.hxx>
|
||||||
|
#include <Geom_OffsetSurface.hxx>
|
||||||
|
#include <Geom_Surface.hxx>
|
||||||
|
#include <Geom_TrimmedCurve.hxx>
|
||||||
|
#include <TColStd_Array1OfInteger.hxx>
|
||||||
|
#include <TColStd_Array1OfReal.hxx>
|
||||||
|
#include <TColgp_Array1OfPnt.hxx>
|
||||||
|
#include <gp_Ax3.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
|
||||||
|
void register_Geom(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "Geom");
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ curves --
|
||||||
|
|
||||||
|
nb::class_<Geom_Curve, Standard_Transient>(m, "Geom_Curve")
|
||||||
|
.def("FirstParameter", &Geom_Curve::FirstParameter)
|
||||||
|
.def("LastParameter", &Geom_Curve::LastParameter)
|
||||||
|
.def("IsClosed", &Geom_Curve::IsClosed)
|
||||||
|
.def("IsPeriodic", &Geom_Curve::IsPeriodic)
|
||||||
|
.def("Value", &Geom_Curve::Value, "U"_a, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<Geom_TrimmedCurve, Geom_Curve>(m, "Geom_TrimmedCurve");
|
||||||
|
|
||||||
|
nb::class_<Geom_BSplineCurve, Geom_Curve>(m, "Geom_BSplineCurve")
|
||||||
|
.def(ocp_new<Geom_BSplineCurve, const TColgp_Array1OfPnt &,
|
||||||
|
const TColStd_Array1OfReal &,
|
||||||
|
const TColStd_Array1OfInteger &, const Standard_Integer,
|
||||||
|
const Standard_Boolean>(),
|
||||||
|
"Poles"_a, "Knots"_a, "Multiplicities"_a, "Degree"_a,
|
||||||
|
"Periodic"_a = Standard_False)
|
||||||
|
.def("Degree", &Geom_BSplineCurve::Degree)
|
||||||
|
.def("NbPoles", &Geom_BSplineCurve::NbPoles)
|
||||||
|
.def("NbKnots", &Geom_BSplineCurve::NbKnots);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------- surfaces --
|
||||||
|
|
||||||
|
nb::class_<Geom_Surface, Standard_Transient>(m, "Geom_Surface")
|
||||||
|
.def("Value", &Geom_Surface::Value, "U"_a, "V"_a, OCP_RETURN_COPY)
|
||||||
|
.def("IsUPeriodic", &Geom_Surface::IsUPeriodic)
|
||||||
|
.def("IsVPeriodic", &Geom_Surface::IsVPeriodic)
|
||||||
|
.def("IsUClosed", &Geom_Surface::IsUClosed)
|
||||||
|
.def("IsVClosed", &Geom_Surface::IsVClosed);
|
||||||
|
|
||||||
|
nb::class_<Geom_BSplineSurface, Geom_Surface>(m, "Geom_BSplineSurface");
|
||||||
|
|
||||||
|
nb::class_<Geom_CylindricalSurface, Geom_Surface>(m,
|
||||||
|
"Geom_CylindricalSurface")
|
||||||
|
.def(ocp_new<Geom_CylindricalSurface, const gp_Ax3 &,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"A3"_a, "Radius"_a)
|
||||||
|
.def("Radius", &Geom_CylindricalSurface::Radius);
|
||||||
|
|
||||||
|
nb::class_<Geom_OffsetSurface, Geom_Surface>(m, "Geom_OffsetSurface")
|
||||||
|
.def(ocp_new<Geom_OffsetSurface, const opencascade::handle<Geom_Surface> &,
|
||||||
|
const Standard_Real, const Standard_Boolean>(),
|
||||||
|
"S"_a, "Offset"_a, "isNotCheckC0"_a = Standard_False)
|
||||||
|
.def("Offset", &Geom_OffsetSurface::Offset)
|
||||||
|
.def("BasisSurface", &Geom_OffsetSurface::BasisSurface);
|
||||||
|
}
|
||||||
30
src/modules/mod_Geom2d.cpp
Normal file
30
src/modules/mod_Geom2d.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
OCP.Geom2d — parametric-space curves.
|
||||||
|
|
||||||
|
Not imported by the app, but unavoidable: GCE2d_MakeLine/MakeSegment return
|
||||||
|
these, and BRepBuilderAPI_MakeEdge takes the Geom2d_Curve base when it
|
||||||
|
builds an edge on a surface (thread.py lays a helix out as a line in the
|
||||||
|
cylinder's UV space). Only registration matters — no method on them is
|
||||||
|
ever called.
|
||||||
|
|
||||||
|
The declared base is Standard_Transient, skipping Geom2d_Geometry: nanobind
|
||||||
|
needs an unambiguous public upcast, not the full chain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <Geom2d_Curve.hxx>
|
||||||
|
#include <Geom2d_Line.hxx>
|
||||||
|
#include <Geom2d_TrimmedCurve.hxx>
|
||||||
|
|
||||||
|
void register_Geom2d(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "Geom2d");
|
||||||
|
|
||||||
|
// Abstract: no constructor. Registered so handles to it convert at all.
|
||||||
|
nb::class_<Geom2d_Curve, Standard_Transient>(m, "Geom2d_Curve")
|
||||||
|
.def("FirstParameter", &Geom2d_Curve::FirstParameter)
|
||||||
|
.def("LastParameter", &Geom2d_Curve::LastParameter);
|
||||||
|
|
||||||
|
nb::class_<Geom2d_Line, Geom2d_Curve>(m, "Geom2d_Line");
|
||||||
|
nb::class_<Geom2d_TrimmedCurve, Geom2d_Curve>(m, "Geom2d_TrimmedCurve");
|
||||||
|
}
|
||||||
59
src/modules/mod_GeomAPI.cpp
Normal file
59
src/modules/mod_GeomAPI.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
OCP.GeomAPI — point projection and surface fitting.
|
||||||
|
|
||||||
|
Both compute in their constructor and expose no deferred Build(), so they
|
||||||
|
are bound exactly as stock does. The no-executing-constructor rule targets
|
||||||
|
the BRepAlgoAPI booleans, where a deferred SetX/Build form exists and the
|
||||||
|
constructor silently duplicates it; it does not apply here (see
|
||||||
|
adding-symbols.md).
|
||||||
|
|
||||||
|
LowerDistanceParameters returns its two scalars as a tuple, which is how
|
||||||
|
the app unpacks it. ProjectPointOnSurf keeps the GIL: it is measured in
|
||||||
|
microseconds and runs inside per-face Python loops, where the release
|
||||||
|
handshake would cost more than the call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <nanobind/stl/tuple.h>
|
||||||
|
|
||||||
|
#include <GeomAPI_PointsToBSplineSurface.hxx>
|
||||||
|
#include <GeomAPI_ProjectPointOnSurf.hxx>
|
||||||
|
#include <GeomAbs_Shape.hxx>
|
||||||
|
#include <Geom_BSplineSurface.hxx>
|
||||||
|
#include <Geom_Surface.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <TColgp_Array2OfPnt.hxx>
|
||||||
|
|
||||||
|
void register_GeomAPI(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "GeomAPI");
|
||||||
|
|
||||||
|
nb::class_<GeomAPI_ProjectPointOnSurf>(m, "GeomAPI_ProjectPointOnSurf")
|
||||||
|
// The Extrema_ExtAlgo overload is left out: binding it would drag in
|
||||||
|
// the Extrema enum only to carry a default the app never overrides,
|
||||||
|
// and an unregistered default argument fails at module-init time.
|
||||||
|
.def(nb::init<const gp_Pnt &, const opencascade::handle<Geom_Surface> &>(),
|
||||||
|
"P"_a, "Surface"_a)
|
||||||
|
.def("IsDone", &GeomAPI_ProjectPointOnSurf::IsDone)
|
||||||
|
.def("NbPoints", &GeomAPI_ProjectPointOnSurf::NbPoints)
|
||||||
|
.def("LowerDistance", &GeomAPI_ProjectPointOnSurf::LowerDistance)
|
||||||
|
.def("NearestPoint", &GeomAPI_ProjectPointOnSurf::NearestPoint,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("LowerDistanceParameters",
|
||||||
|
[](const GeomAPI_ProjectPointOnSurf &self) {
|
||||||
|
Standard_Real u = 0.0, v = 0.0;
|
||||||
|
self.LowerDistanceParameters(u, v);
|
||||||
|
return std::make_tuple(u, v);
|
||||||
|
});
|
||||||
|
|
||||||
|
nb::class_<GeomAPI_PointsToBSplineSurface>(m, "GeomAPI_PointsToBSplineSurface")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const TColgp_Array2OfPnt &, const Standard_Integer,
|
||||||
|
const Standard_Integer, const GeomAbs_Shape,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"Points"_a, "DegMin"_a = 3, "DegMax"_a = 8,
|
||||||
|
"Continuity"_a = GeomAbs_C2, "Tol3D"_a = 1.0e-3, OCP_NOGIL)
|
||||||
|
.def("IsDone", &GeomAPI_PointsToBSplineSurface::IsDone)
|
||||||
|
.def("Surface", &GeomAPI_PointsToBSplineSurface::Surface);
|
||||||
|
}
|
||||||
55
src/modules/mod_GeomAbs.cpp
Normal file
55
src/modules/mod_GeomAbs.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
OCP.GeomAbs — the curve/surface type tags the adaptors report.
|
||||||
|
|
||||||
|
The app reads these off BRepAdaptor_Curve/Surface.GetType() to decide what
|
||||||
|
a face or edge *is* (plane, cylinder, circle, spline), which drives
|
||||||
|
reference inference, anchor scoring and hole recognition. Only the
|
||||||
|
continuity value GeomAbs_C2 is used as an input, by
|
||||||
|
GeomAPI_PointsToBSplineSurface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <GeomAbs_CurveType.hxx>
|
||||||
|
#include <GeomAbs_Shape.hxx>
|
||||||
|
#include <GeomAbs_SurfaceType.hxx>
|
||||||
|
|
||||||
|
void register_GeomAbs(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "GeomAbs");
|
||||||
|
|
||||||
|
nb::enum_<GeomAbs_CurveType>(m, "GeomAbs_CurveType", nb::is_arithmetic())
|
||||||
|
.value("GeomAbs_Line", GeomAbs_Line)
|
||||||
|
.value("GeomAbs_Circle", GeomAbs_Circle)
|
||||||
|
.value("GeomAbs_Ellipse", GeomAbs_Ellipse)
|
||||||
|
.value("GeomAbs_Hyperbola", GeomAbs_Hyperbola)
|
||||||
|
.value("GeomAbs_Parabola", GeomAbs_Parabola)
|
||||||
|
.value("GeomAbs_BezierCurve", GeomAbs_BezierCurve)
|
||||||
|
.value("GeomAbs_BSplineCurve", GeomAbs_BSplineCurve)
|
||||||
|
.value("GeomAbs_OffsetCurve", GeomAbs_OffsetCurve)
|
||||||
|
.value("GeomAbs_OtherCurve", GeomAbs_OtherCurve)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
nb::enum_<GeomAbs_SurfaceType>(m, "GeomAbs_SurfaceType", nb::is_arithmetic())
|
||||||
|
.value("GeomAbs_Plane", GeomAbs_Plane)
|
||||||
|
.value("GeomAbs_Cylinder", GeomAbs_Cylinder)
|
||||||
|
.value("GeomAbs_Cone", GeomAbs_Cone)
|
||||||
|
.value("GeomAbs_Sphere", GeomAbs_Sphere)
|
||||||
|
.value("GeomAbs_Torus", GeomAbs_Torus)
|
||||||
|
.value("GeomAbs_BezierSurface", GeomAbs_BezierSurface)
|
||||||
|
.value("GeomAbs_BSplineSurface", GeomAbs_BSplineSurface)
|
||||||
|
.value("GeomAbs_SurfaceOfRevolution", GeomAbs_SurfaceOfRevolution)
|
||||||
|
.value("GeomAbs_SurfaceOfExtrusion", GeomAbs_SurfaceOfExtrusion)
|
||||||
|
.value("GeomAbs_OffsetSurface", GeomAbs_OffsetSurface)
|
||||||
|
.value("GeomAbs_OtherSurface", GeomAbs_OtherSurface)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
nb::enum_<GeomAbs_Shape>(m, "GeomAbs_Shape", nb::is_arithmetic())
|
||||||
|
.value("GeomAbs_C0", GeomAbs_C0)
|
||||||
|
.value("GeomAbs_G1", GeomAbs_G1)
|
||||||
|
.value("GeomAbs_C1", GeomAbs_C1)
|
||||||
|
.value("GeomAbs_G2", GeomAbs_G2)
|
||||||
|
.value("GeomAbs_C2", GeomAbs_C2)
|
||||||
|
.value("GeomAbs_C3", GeomAbs_C3)
|
||||||
|
.value("GeomAbs_CN", GeomAbs_CN)
|
||||||
|
.export_values();
|
||||||
|
}
|
||||||
32
src/modules/mod_GeomLProp.cpp
Normal file
32
src/modules/mod_GeomLProp.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
OCP.GeomLProp — local surface properties.
|
||||||
|
|
||||||
|
One use: text emboss reads the surface normal at the anchor's UV to decide
|
||||||
|
which way the glyph solid grows out of a curved face.
|
||||||
|
|
||||||
|
OCCT 8.0 supersedes this package with GeomProp/BRepProp (results come back
|
||||||
|
as structs carrying IsDefined flags instead of throwing). Only Normal()
|
||||||
|
is bound, so the port is a one-line move — see design.md's 8.0 watchlist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <GeomLProp_SLProps.hxx>
|
||||||
|
#include <Geom_Surface.hxx>
|
||||||
|
#include <gp_Dir.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
|
||||||
|
void register_GeomLProp(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "GeomLProp");
|
||||||
|
|
||||||
|
nb::class_<GeomLProp_SLProps>(m, "GeomLProp_SLProps")
|
||||||
|
.def(nb::init<const opencascade::handle<Geom_Surface> &, const Standard_Real,
|
||||||
|
const Standard_Real, const Standard_Integer,
|
||||||
|
const Standard_Real>(),
|
||||||
|
"S"_a, "U"_a, "V"_a, "N"_a, "Resolution"_a)
|
||||||
|
.def("Value", &GeomLProp_SLProps::Value, OCP_RETURN_COPY)
|
||||||
|
.def("Normal", &GeomLProp_SLProps::Normal, OCP_RETURN_COPY)
|
||||||
|
.def("IsNormalDefined", &GeomLProp_SLProps::IsNormalDefined)
|
||||||
|
.def("SetParameters", &GeomLProp_SLProps::SetParameters, "U"_a, "V"_a);
|
||||||
|
}
|
||||||
67
src/modules/mod_TColStd.cpp
Normal file
67
src/modules/mod_TColStd.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
OCP.TColStd — the plain integer/real arrays a B-spline definition needs
|
||||||
|
(knots and multiplicities in sketch_builder/edges.py), plus the string
|
||||||
|
sequence the IGES reader fills with unit names.
|
||||||
|
|
||||||
|
NCollection's SetValue/Value are overloaded (const ref and rvalue), so they
|
||||||
|
are bound through lambdas rather than member pointers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
|
#include <TColStd_Array1OfInteger.hxx>
|
||||||
|
#include <TColStd_Array1OfReal.hxx>
|
||||||
|
#include <TColStd_SequenceOfAsciiString.hxx>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// Bind one NCollection_Array1<T> instantiation with the app's surface.
|
||||||
|
template <typename A, typename T>
|
||||||
|
void bind_array1(nb::module_ &m, const char *name) {
|
||||||
|
nb::class_<A>(m, name)
|
||||||
|
.def(nb::init<const Standard_Integer, const Standard_Integer>(),
|
||||||
|
"theLower"_a, "theUpper"_a)
|
||||||
|
.def(
|
||||||
|
"SetValue",
|
||||||
|
[](A &self, Standard_Integer index, const T &value) {
|
||||||
|
self.SetValue(index, value);
|
||||||
|
},
|
||||||
|
"theIndex"_a, "theValue"_a)
|
||||||
|
.def(
|
||||||
|
"Value",
|
||||||
|
[](const A &self, Standard_Integer index) { return self.Value(index); },
|
||||||
|
"theIndex"_a)
|
||||||
|
.def("Length", &A::Length)
|
||||||
|
.def("Lower", &A::Lower)
|
||||||
|
.def("Upper", &A::Upper)
|
||||||
|
.def("__len__", &A::Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void register_TColStd(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "TColStd");
|
||||||
|
|
||||||
|
bind_array1<TColStd_Array1OfInteger, Standard_Integer>(
|
||||||
|
m, "TColStd_Array1OfInteger");
|
||||||
|
bind_array1<TColStd_Array1OfReal, Standard_Real>(m, "TColStd_Array1OfReal");
|
||||||
|
|
||||||
|
nb::class_<TColStd_SequenceOfAsciiString>(m, "TColStd_SequenceOfAsciiString")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("Length", &TColStd_SequenceOfAsciiString::Length)
|
||||||
|
.def("IsEmpty", &TColStd_SequenceOfAsciiString::IsEmpty)
|
||||||
|
.def(
|
||||||
|
"Value",
|
||||||
|
[](const TColStd_SequenceOfAsciiString &self, Standard_Integer i) {
|
||||||
|
return self.Value(i);
|
||||||
|
},
|
||||||
|
"theIndex"_a)
|
||||||
|
.def(
|
||||||
|
"Append",
|
||||||
|
[](TColStd_SequenceOfAsciiString &self,
|
||||||
|
const TCollection_AsciiString &v) { self.Append(v); },
|
||||||
|
"theItem"_a)
|
||||||
|
.def("Clear", [](TColStd_SequenceOfAsciiString &self) { self.Clear(); })
|
||||||
|
.def("__len__", &TColStd_SequenceOfAsciiString::Length);
|
||||||
|
}
|
||||||
59
src/modules/mod_TColgp.cpp
Normal file
59
src/modules/mod_TColgp.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
OCP.TColgp — point arrays.
|
||||||
|
|
||||||
|
Array1 carries B-spline poles (sketch_builder/edges.py); Array2 carries the
|
||||||
|
control grid GeomAPI_PointsToBSplineSurface fits a smooth surface through.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <TColgp_Array1OfPnt.hxx>
|
||||||
|
#include <TColgp_Array2OfPnt.hxx>
|
||||||
|
#include <gp_Pnt.hxx>
|
||||||
|
|
||||||
|
void register_TColgp(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "TColgp");
|
||||||
|
|
||||||
|
nb::class_<TColgp_Array1OfPnt>(m, "TColgp_Array1OfPnt")
|
||||||
|
.def(nb::init<const Standard_Integer, const Standard_Integer>(),
|
||||||
|
"theLower"_a, "theUpper"_a)
|
||||||
|
.def(
|
||||||
|
"SetValue",
|
||||||
|
[](TColgp_Array1OfPnt &self, Standard_Integer i, const gp_Pnt &v) {
|
||||||
|
self.SetValue(i, v);
|
||||||
|
},
|
||||||
|
"theIndex"_a, "theValue"_a)
|
||||||
|
.def(
|
||||||
|
"Value",
|
||||||
|
[](const TColgp_Array1OfPnt &self, Standard_Integer i) {
|
||||||
|
return self.Value(i);
|
||||||
|
},
|
||||||
|
"theIndex"_a)
|
||||||
|
.def("Length", &TColgp_Array1OfPnt::Length)
|
||||||
|
.def("Lower", &TColgp_Array1OfPnt::Lower)
|
||||||
|
.def("Upper", &TColgp_Array1OfPnt::Upper)
|
||||||
|
.def("__len__", &TColgp_Array1OfPnt::Length);
|
||||||
|
|
||||||
|
nb::class_<TColgp_Array2OfPnt>(m, "TColgp_Array2OfPnt")
|
||||||
|
.def(nb::init<const Standard_Integer, const Standard_Integer,
|
||||||
|
const Standard_Integer, const Standard_Integer>(),
|
||||||
|
"theRowLower"_a, "theRowUpper"_a, "theColLower"_a, "theColUpper"_a)
|
||||||
|
.def(
|
||||||
|
"SetValue",
|
||||||
|
[](TColgp_Array2OfPnt &self, Standard_Integer row,
|
||||||
|
Standard_Integer col, const gp_Pnt &v) {
|
||||||
|
self.SetValue(row, col, v);
|
||||||
|
},
|
||||||
|
"theRow"_a, "theCol"_a, "theValue"_a)
|
||||||
|
.def(
|
||||||
|
"Value",
|
||||||
|
[](const TColgp_Array2OfPnt &self, Standard_Integer row,
|
||||||
|
Standard_Integer col) { return self.Value(row, col); },
|
||||||
|
"theRow"_a, "theCol"_a)
|
||||||
|
.def("RowLength", &TColgp_Array2OfPnt::RowLength)
|
||||||
|
.def("ColLength", &TColgp_Array2OfPnt::ColLength)
|
||||||
|
.def("LowerRow", &TColgp_Array2OfPnt::LowerRow)
|
||||||
|
.def("UpperRow", &TColgp_Array2OfPnt::UpperRow)
|
||||||
|
.def("LowerCol", &TColgp_Array2OfPnt::LowerCol)
|
||||||
|
.def("UpperCol", &TColgp_Array2OfPnt::UpperCol);
|
||||||
|
}
|
||||||
24
src/modules/mod_TCollection.cpp
Normal file
24
src/modules/mod_TCollection.cpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
OCP.TCollection — OCCT's own string type.
|
||||||
|
|
||||||
|
Not imported by the app directly: it arrives as the element type of
|
||||||
|
TColStd_SequenceOfAsciiString, which the IGES reader fills with unit names
|
||||||
|
(`import_.py` reads them back through ToCString).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common/occt_module.h"
|
||||||
|
|
||||||
|
#include <TCollection_AsciiString.hxx>
|
||||||
|
|
||||||
|
void register_TCollection(nb::module_ &root) {
|
||||||
|
nb::module_ m = ocp_submodule(root, "TCollection");
|
||||||
|
|
||||||
|
nb::class_<TCollection_AsciiString>(m, "TCollection_AsciiString")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const Standard_CString>(), "message"_a)
|
||||||
|
.def("ToCString", &TCollection_AsciiString::ToCString)
|
||||||
|
.def("Length", &TCollection_AsciiString::Length)
|
||||||
|
.def("IsEmpty", &TCollection_AsciiString::IsEmpty)
|
||||||
|
.def("__len__", &TCollection_AsciiString::Length)
|
||||||
|
.def("__str__", &TCollection_AsciiString::ToCString);
|
||||||
|
}
|
||||||
@@ -1,19 +1,33 @@
|
|||||||
/*
|
/*
|
||||||
OCP.gp — the Inc 0 subset (points, vectors, directions, axes, transforms).
|
OCP.gp — points, vectors, directions, axes, planes and transforms.
|
||||||
|
|
||||||
The rest of gp (gp_Ax2/Ax3/Pln/Circ/Lin/Pnt2d/Dir2d/Quaternion, which the
|
The quadrics (gp_Cylinder/Cone/Sphere/Elips) are not imported by the app
|
||||||
app also uses) lands with Inc 1, where the curve and surface classes that
|
directly; they arrive as return values of the adaptors' GetType() branches
|
||||||
consume them arrive.
|
(`BRepAdaptor_Surface(f).Cylinder().Radius()`), so they have to be
|
||||||
|
registered for those returns to convert at all.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../common/occt_module.h"
|
#include "../common/occt_module.h"
|
||||||
#include "../common/occt_policies.h"
|
#include "../common/occt_policies.h"
|
||||||
|
|
||||||
#include <gp_Ax1.hxx>
|
#include <gp_Ax1.hxx>
|
||||||
|
#include <gp_Ax2.hxx>
|
||||||
|
#include <gp_Ax3.hxx>
|
||||||
|
#include <gp_Circ.hxx>
|
||||||
|
#include <gp_Cone.hxx>
|
||||||
|
#include <gp_Cylinder.hxx>
|
||||||
#include <gp_Dir.hxx>
|
#include <gp_Dir.hxx>
|
||||||
|
#include <gp_Dir2d.hxx>
|
||||||
|
#include <gp_Elips.hxx>
|
||||||
|
#include <gp_Lin.hxx>
|
||||||
|
#include <gp_Pln.hxx>
|
||||||
#include <gp_Pnt.hxx>
|
#include <gp_Pnt.hxx>
|
||||||
|
#include <gp_Pnt2d.hxx>
|
||||||
|
#include <gp_Quaternion.hxx>
|
||||||
|
#include <gp_Sphere.hxx>
|
||||||
#include <gp_Trsf.hxx>
|
#include <gp_Trsf.hxx>
|
||||||
#include <gp_Vec.hxx>
|
#include <gp_Vec.hxx>
|
||||||
|
#include <gp_Vec2d.hxx>
|
||||||
#include <gp_XYZ.hxx>
|
#include <gp_XYZ.hxx>
|
||||||
|
|
||||||
void register_gp(nb::module_ &root) {
|
void register_gp(nb::module_ &root) {
|
||||||
@@ -81,6 +95,9 @@ void register_gp(nb::module_ &root) {
|
|||||||
.def("Reversed", &gp_Vec::Reversed, OCP_RETURN_COPY)
|
.def("Reversed", &gp_Vec::Reversed, OCP_RETURN_COPY)
|
||||||
.def("Multiplied", &gp_Vec::Multiplied, "Scalar"_a, OCP_RETURN_COPY)
|
.def("Multiplied", &gp_Vec::Multiplied, "Scalar"_a, OCP_RETURN_COPY)
|
||||||
.def("Angle", &gp_Vec::Angle, "Other"_a)
|
.def("Angle", &gp_Vec::Angle, "Other"_a)
|
||||||
|
.def("Added", &gp_Vec::Added, "Other"_a, OCP_RETURN_COPY)
|
||||||
|
.def("Subtracted", &gp_Vec::Subtracted, "Right"_a, OCP_RETURN_COPY)
|
||||||
|
.def("Multiply", &gp_Vec::Multiply, "Scalar"_a)
|
||||||
.def("Transform", &gp_Vec::Transform, "T"_a)
|
.def("Transform", &gp_Vec::Transform, "T"_a)
|
||||||
.def("Transformed", &gp_Vec::Transformed, "T"_a, OCP_RETURN_COPY);
|
.def("Transformed", &gp_Vec::Transformed, "T"_a, OCP_RETURN_COPY);
|
||||||
|
|
||||||
@@ -123,6 +140,9 @@ void register_gp(nb::module_ &root) {
|
|||||||
nb::overload_cast<const gp_Ax1 &, Standard_Real>(
|
nb::overload_cast<const gp_Ax1 &, Standard_Real>(
|
||||||
&gp_Trsf::SetRotation),
|
&gp_Trsf::SetRotation),
|
||||||
"A1"_a, "Ang"_a)
|
"A1"_a, "Ang"_a)
|
||||||
|
.def("SetRotation",
|
||||||
|
nb::overload_cast<const gp_Quaternion &>(&gp_Trsf::SetRotation),
|
||||||
|
"R"_a)
|
||||||
.def("SetScale", &gp_Trsf::SetScale, "P"_a, "S"_a)
|
.def("SetScale", &gp_Trsf::SetScale, "P"_a, "S"_a)
|
||||||
.def("SetMirror", nb::overload_cast<const gp_Ax1 &>(&gp_Trsf::SetMirror),
|
.def("SetMirror", nb::overload_cast<const gp_Ax1 &>(&gp_Trsf::SetMirror),
|
||||||
"A1"_a)
|
"A1"_a)
|
||||||
@@ -135,4 +155,160 @@ void register_gp(nb::module_ &root) {
|
|||||||
.def("Multiply", &gp_Trsf::Multiply, "T"_a)
|
.def("Multiply", &gp_Trsf::Multiply, "T"_a)
|
||||||
.def("PreMultiply", &gp_Trsf::PreMultiply, "T"_a)
|
.def("PreMultiply", &gp_Trsf::PreMultiply, "T"_a)
|
||||||
.def("Invert", &gp_Trsf::Invert);
|
.def("Invert", &gp_Trsf::Invert);
|
||||||
|
|
||||||
|
nb::class_<gp_Quaternion>(m, "gp_Quaternion")
|
||||||
|
.def(nb::init<>())
|
||||||
|
// (x, y, z, w) — note the scalar comes *last*, which is the opposite
|
||||||
|
// of the (w, x, y, z) order most serialisations use.
|
||||||
|
.def(nb::init<Standard_Real, Standard_Real, Standard_Real,
|
||||||
|
Standard_Real>(),
|
||||||
|
"x"_a, "y"_a, "z"_a, "w"_a)
|
||||||
|
.def(nb::init<const gp_Vec &, Standard_Real>(), "theAxis"_a,
|
||||||
|
"theAngle"_a)
|
||||||
|
.def("X", &gp_Quaternion::X)
|
||||||
|
.def("Y", &gp_Quaternion::Y)
|
||||||
|
.def("Z", &gp_Quaternion::Z)
|
||||||
|
.def("W", &gp_Quaternion::W)
|
||||||
|
.def("Normalize", &gp_Quaternion::Normalize)
|
||||||
|
.def("Normalized", &gp_Quaternion::Normalized, OCP_RETURN_COPY)
|
||||||
|
.def("GetRotationAngle", &gp_Quaternion::GetRotationAngle);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- 2D --
|
||||||
|
|
||||||
|
nb::class_<gp_Pnt2d>(m, "gp_Pnt2d")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<Standard_Real, Standard_Real>(), "Xp"_a, "Yp"_a)
|
||||||
|
.def("X", &gp_Pnt2d::X)
|
||||||
|
.def("Y", &gp_Pnt2d::Y)
|
||||||
|
.def("SetX", &gp_Pnt2d::SetX, "X"_a)
|
||||||
|
.def("SetY", &gp_Pnt2d::SetY, "Y"_a)
|
||||||
|
.def("Distance", &gp_Pnt2d::Distance, "Other"_a);
|
||||||
|
|
||||||
|
nb::class_<gp_Dir2d>(m, "gp_Dir2d")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<Standard_Real, Standard_Real>(), "Xv"_a, "Yv"_a)
|
||||||
|
.def("X", &gp_Dir2d::X)
|
||||||
|
.def("Y", &gp_Dir2d::Y)
|
||||||
|
.def("Angle", &gp_Dir2d::Angle, "Other"_a)
|
||||||
|
.def("Reversed", &gp_Dir2d::Reversed, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<gp_Vec2d>(m, "gp_Vec2d")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<Standard_Real, Standard_Real>(), "Xv"_a, "Yv"_a)
|
||||||
|
.def(nb::init<const gp_Dir2d &>(), "V"_a)
|
||||||
|
.def("X", &gp_Vec2d::X)
|
||||||
|
.def("Y", &gp_Vec2d::Y)
|
||||||
|
.def("Magnitude", &gp_Vec2d::Magnitude);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------- axes --
|
||||||
|
|
||||||
|
nb::class_<gp_Ax2>(m, "gp_Ax2")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &>(), "P"_a, "V"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &, const gp_Dir &>(), "P"_a,
|
||||||
|
"N"_a, "Vx"_a)
|
||||||
|
.def("Location", &gp_Ax2::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Direction", &gp_Ax2::Direction, OCP_RETURN_COPY)
|
||||||
|
.def("XDirection", &gp_Ax2::XDirection, OCP_RETURN_COPY)
|
||||||
|
.def("YDirection", &gp_Ax2::YDirection, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Ax2::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("SetLocation", &gp_Ax2::SetLocation, "theP"_a)
|
||||||
|
.def("SetDirection", &gp_Ax2::SetDirection, "theV"_a)
|
||||||
|
.def("Rotate", &gp_Ax2::Rotate, "theA1"_a, "theAng"_a)
|
||||||
|
.def("Rotated", &gp_Ax2::Rotated, "theA1"_a, "theAng"_a,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("Transform", &gp_Ax2::Transform, "theT"_a)
|
||||||
|
.def("Transformed", &gp_Ax2::Transformed, "theT"_a, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<gp_Ax3>(m, "gp_Ax3")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Ax2 &>(), "theA"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &>(), "theP"_a, "theN"_a)
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &, const gp_Dir &>(),
|
||||||
|
"theP"_a, "theN"_a, "theVx"_a)
|
||||||
|
.def("Location", &gp_Ax3::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Direction", &gp_Ax3::Direction, OCP_RETURN_COPY)
|
||||||
|
.def("XDirection", &gp_Ax3::XDirection, OCP_RETURN_COPY)
|
||||||
|
.def("YDirection", &gp_Ax3::YDirection, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Ax3::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Ax2", &gp_Ax3::Ax2, OCP_RETURN_COPY)
|
||||||
|
.def("Direct", &gp_Ax3::Direct)
|
||||||
|
.def("SetLocation", &gp_Ax3::SetLocation, "theP"_a)
|
||||||
|
.def("SetDirection", &gp_Ax3::SetDirection, "theV"_a)
|
||||||
|
.def("Rotate", &gp_Ax3::Rotate, "theA1"_a, "theAng"_a)
|
||||||
|
.def("Rotated", &gp_Ax3::Rotated, "theA1"_a, "theAng"_a,
|
||||||
|
OCP_RETURN_COPY)
|
||||||
|
.def("Transform", &gp_Ax3::Transform, "theT"_a)
|
||||||
|
.def("Transformed", &gp_Ax3::Transformed, "theT"_a, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
// ------------------------------------------------- curves & quadrics --
|
||||||
|
|
||||||
|
nb::class_<gp_Lin>(m, "gp_Lin")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &>(), "theP"_a, "theV"_a)
|
||||||
|
.def(nb::init<const gp_Ax1 &>(), "theA1"_a)
|
||||||
|
.def("Location", &gp_Lin::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Direction", &gp_Lin::Direction, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Lin::Position, OCP_RETURN_COPY)
|
||||||
|
.def("Distance", nb::overload_cast<const gp_Pnt &>(&gp_Lin::Distance,
|
||||||
|
nb::const_),
|
||||||
|
"theP"_a);
|
||||||
|
|
||||||
|
nb::class_<gp_Circ>(m, "gp_Circ")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Ax2 &, Standard_Real>(), "theA2"_a,
|
||||||
|
"theRadius"_a)
|
||||||
|
.def("Location", &gp_Circ::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Circ::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Circ::Position, OCP_RETURN_COPY)
|
||||||
|
.def("Radius", &gp_Circ::Radius);
|
||||||
|
|
||||||
|
nb::class_<gp_Elips>(m, "gp_Elips")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("Location", &gp_Elips::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Elips::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Elips::Position, OCP_RETURN_COPY)
|
||||||
|
.def("MajorRadius", &gp_Elips::MajorRadius)
|
||||||
|
.def("MinorRadius", &gp_Elips::MinorRadius);
|
||||||
|
|
||||||
|
nb::class_<gp_Pln>(m, "gp_Pln")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Pnt &, const gp_Dir &>(), "theP"_a, "theV"_a)
|
||||||
|
.def(nb::init<const gp_Ax3 &>(), "theA3"_a)
|
||||||
|
.def("Location", &gp_Pln::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Pln::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Pln::Position, OCP_RETURN_COPY)
|
||||||
|
.def("XAxis", &gp_Pln::XAxis, OCP_RETURN_COPY)
|
||||||
|
.def("YAxis", &gp_Pln::YAxis, OCP_RETURN_COPY)
|
||||||
|
.def("Distance", nb::overload_cast<const gp_Pnt &>(&gp_Pln::Distance,
|
||||||
|
nb::const_),
|
||||||
|
"theP"_a)
|
||||||
|
.def("Transform", &gp_Pln::Transform, "theT"_a)
|
||||||
|
.def("Transformed", &gp_Pln::Transformed, "theT"_a, OCP_RETURN_COPY);
|
||||||
|
|
||||||
|
nb::class_<gp_Cylinder>(m, "gp_Cylinder")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Ax3 &, Standard_Real>(), "theA3"_a,
|
||||||
|
"theRadius"_a)
|
||||||
|
.def("Location", &gp_Cylinder::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Cylinder::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Cylinder::Position, OCP_RETURN_COPY)
|
||||||
|
.def("Radius", &gp_Cylinder::Radius);
|
||||||
|
|
||||||
|
nb::class_<gp_Cone>(m, "gp_Cone")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def("Location", &gp_Cone::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Axis", &gp_Cone::Axis, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Cone::Position, OCP_RETURN_COPY)
|
||||||
|
.def("Apex", &gp_Cone::Apex, OCP_RETURN_COPY)
|
||||||
|
.def("RefRadius", &gp_Cone::RefRadius)
|
||||||
|
.def("SemiAngle", &gp_Cone::SemiAngle);
|
||||||
|
|
||||||
|
nb::class_<gp_Sphere>(m, "gp_Sphere")
|
||||||
|
.def(nb::init<>())
|
||||||
|
.def(nb::init<const gp_Ax3 &, Standard_Real>(), "theA3"_a,
|
||||||
|
"theRadius"_a)
|
||||||
|
.def("Location", &gp_Sphere::Location, OCP_RETURN_COPY)
|
||||||
|
.def("Position", &gp_Sphere::Position, OCP_RETURN_COPY)
|
||||||
|
.def("Radius", &gp_Sphere::Radius);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,472 @@
|
|||||||
"size": 171,
|
"size": 171,
|
||||||
"faces": 0
|
"faces": 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"inc1": {
|
||||||
|
"measure": {
|
||||||
|
"box": {
|
||||||
|
"volume": 6000.0,
|
||||||
|
"area": 2200.0,
|
||||||
|
"bbox": [
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
10.0000001,
|
||||||
|
20.0000001,
|
||||||
|
30.0000001
|
||||||
|
],
|
||||||
|
"n_faces": 6,
|
||||||
|
"n_edges": 12,
|
||||||
|
"n_solids": 1
|
||||||
|
},
|
||||||
|
"box_meshed": {
|
||||||
|
"volume": 6000.0,
|
||||||
|
"area": 2200.0,
|
||||||
|
"bbox": [
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
10.0000001,
|
||||||
|
20.0000001,
|
||||||
|
30.0000001
|
||||||
|
],
|
||||||
|
"n_faces": 6,
|
||||||
|
"n_edges": 12,
|
||||||
|
"n_solids": 1
|
||||||
|
},
|
||||||
|
"fused": {
|
||||||
|
"volume": 12500.0,
|
||||||
|
"area": 3650.0,
|
||||||
|
"bbox": [
|
||||||
|
-1.5e-07,
|
||||||
|
-1.5e-07,
|
||||||
|
-1.5e-07,
|
||||||
|
25.00000015,
|
||||||
|
25.00000015,
|
||||||
|
30.00000015
|
||||||
|
],
|
||||||
|
"n_faces": 12,
|
||||||
|
"n_edges": 30,
|
||||||
|
"n_solids": 1
|
||||||
|
},
|
||||||
|
"cut_cylinder": {
|
||||||
|
"volume": 5151.769983530756,
|
||||||
|
"area": 2708.9380098815463,
|
||||||
|
"bbox": [
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
10.0000001,
|
||||||
|
20.0000001,
|
||||||
|
30.0000001
|
||||||
|
],
|
||||||
|
"n_faces": 7,
|
||||||
|
"n_edges": 15,
|
||||||
|
"n_solids": 1
|
||||||
|
},
|
||||||
|
"located_compound": {
|
||||||
|
"volume": 12000.0,
|
||||||
|
"area": 4400.0,
|
||||||
|
"bbox": [
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
-1e-07,
|
||||||
|
60.0000001,
|
||||||
|
20.0000001,
|
||||||
|
30.0000001
|
||||||
|
],
|
||||||
|
"n_faces": 12,
|
||||||
|
"n_edges": 24,
|
||||||
|
"n_solids": 2
|
||||||
|
},
|
||||||
|
"empty_compound": {
|
||||||
|
"volume": 0.0,
|
||||||
|
"area": 0.0,
|
||||||
|
"bbox": null,
|
||||||
|
"n_faces": 0,
|
||||||
|
"n_edges": 0,
|
||||||
|
"n_solids": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"face_props": {
|
||||||
|
"box": [
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
10.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
30.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"box_meshed": [
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
10.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
30.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"fused": [
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
30.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
200.0,
|
||||||
|
3.7500000000000004,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
10.0,
|
||||||
|
7.5,
|
||||||
|
14.999999999999996
|
||||||
|
],
|
||||||
|
[
|
||||||
|
324.99999999999994,
|
||||||
|
16.73076923076923,
|
||||||
|
15.57692307692308,
|
||||||
|
5.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
99.99999999999999,
|
||||||
|
5.0,
|
||||||
|
22.5,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
324.99999999999994,
|
||||||
|
16.73076923076923,
|
||||||
|
15.57692307692308,
|
||||||
|
25.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
299.99999999999994,
|
||||||
|
17.5,
|
||||||
|
5.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
399.99999999999994,
|
||||||
|
15.0,
|
||||||
|
25.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
399.99999999999994,
|
||||||
|
25.0,
|
||||||
|
15.0,
|
||||||
|
15.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"cut_cylinder": [
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
171.72566611769184,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
30.000000000000004
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
171.72566611769184,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
10.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
565.4866776461628,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
15.000000000000002
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"located_compound": [
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
10.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
5.0,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
5.0,
|
||||||
|
10.0,
|
||||||
|
30.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
50.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
600.0,
|
||||||
|
60.0,
|
||||||
|
10.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
55.0,
|
||||||
|
0.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
300.0,
|
||||||
|
55.0,
|
||||||
|
20.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
55.0,
|
||||||
|
10.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199.99999999999997,
|
||||||
|
55.0,
|
||||||
|
10.0,
|
||||||
|
30.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cut_cylinder_history": [
|
||||||
|
{
|
||||||
|
"pre": 0,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [],
|
||||||
|
"generated": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pre": 1,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [],
|
||||||
|
"generated": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pre": 2,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [],
|
||||||
|
"generated": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pre": 3,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [],
|
||||||
|
"generated": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pre": 4,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [
|
||||||
|
33
|
||||||
|
],
|
||||||
|
"generated": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pre": 5,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"generated": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"box_meshed_counts": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"constructions": {
|
||||||
|
"arc_edge": {
|
||||||
|
"sha256": "2b6eadbe678c8e13d04cfa1fdf6d6d475776d168639dbb38b90452c9f1481535",
|
||||||
|
"length": 15.707963267948966
|
||||||
|
},
|
||||||
|
"spline_edge": {
|
||||||
|
"sha256": "71a9b3c8aee46b338728fc03b98931d4087c5df5962e9745d70fcfd3cc1a3133",
|
||||||
|
"length": 12.236066959998437
|
||||||
|
},
|
||||||
|
"prism": {
|
||||||
|
"sha256": "0730ad0a74dda44760b4a43b4b7f22ed58c672d8aeb8f3fd483969b92cbf3d2f",
|
||||||
|
"volume": 239.99999999999997,
|
||||||
|
"area": 247.99999999999994,
|
||||||
|
"bbox": [
|
||||||
|
-1.0000000000000002e-07,
|
||||||
|
-1.0000000000000002e-07,
|
||||||
|
-1.0000000000000002e-07,
|
||||||
|
10.0000001,
|
||||||
|
6.0000001,
|
||||||
|
4.0000001
|
||||||
|
],
|
||||||
|
"n_faces": 6,
|
||||||
|
"n_edges": 12,
|
||||||
|
"n_solids": 1,
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"pre": 0,
|
||||||
|
"deleted": false,
|
||||||
|
"modified": [],
|
||||||
|
"generated": [
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
386
tests/test_inc1_modeling.py
Normal file
386
tests/test_inc1_modeling.py
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
"""Inc 1 gate: core modeling reproduces the stock wheel's answers.
|
||||||
|
|
||||||
|
The app's own suite cannot run until the last module is bound (its conftest
|
||||||
|
imports n3xd.main, i.e. the whole OCP surface), so an increment is gated on the
|
||||||
|
reference block tools/gen_fixtures.py records from the stock wheel.
|
||||||
|
|
||||||
|
What is compared, and why in that form:
|
||||||
|
|
||||||
|
* counts and boolean history maps — **exactly**. They are topological, so no
|
||||||
|
tolerance is meaningful, and the history *is* the topological-naming
|
||||||
|
substrate: the app decides which feature owns which face from it.
|
||||||
|
* measurements — at a relative tolerance. Same kernel and same conservative
|
||||||
|
optimiser flags, so they agree to far better than this; the tolerance is
|
||||||
|
there so a last-ulp difference is not reported as a parity failure.
|
||||||
|
* constructed shapes — by BREP digest, which is the strongest available
|
||||||
|
statement and the one cad/derive.py's content addressing depends on.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from OCP.Bnd import Bnd_Box
|
||||||
|
from OCP.BRep import BRep_Tool
|
||||||
|
from OCP.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface
|
||||||
|
from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse
|
||||||
|
from OCP.BRepBndLib import BRepBndLib
|
||||||
|
from OCP.BRepBuilderAPI import (
|
||||||
|
BRepBuilderAPI_MakeEdge,
|
||||||
|
BRepBuilderAPI_MakeFace,
|
||||||
|
BRepBuilderAPI_MakePolygon,
|
||||||
|
)
|
||||||
|
from OCP.BRepGProp import BRepGProp
|
||||||
|
from OCP.BRepMesh import BRepMesh_IncrementalMesh
|
||||||
|
from OCP.BRepPrimAPI import (
|
||||||
|
BRepPrimAPI_MakeBox,
|
||||||
|
BRepPrimAPI_MakeCylinder,
|
||||||
|
BRepPrimAPI_MakePrism,
|
||||||
|
)
|
||||||
|
from OCP.BinTools import BinTools
|
||||||
|
from OCP.GC import GC_MakeArcOfCircle
|
||||||
|
from OCP.Geom import Geom_BSplineCurve
|
||||||
|
from OCP.GeomAbs import GeomAbs_Cylinder, GeomAbs_Line, GeomAbs_Plane
|
||||||
|
from OCP.GProp import GProp_GProps
|
||||||
|
from OCP.gp import gp_Ax2, gp_Dir, gp_Pnt, gp_Vec
|
||||||
|
from OCP.TColgp import TColgp_Array1OfPnt
|
||||||
|
from OCP.TColStd import TColStd_Array1OfInteger, TColStd_Array1OfReal
|
||||||
|
from OCP.TopAbs import TopAbs_EDGE, TopAbs_FACE, TopAbs_SOLID
|
||||||
|
from OCP.TopExp import TopExp
|
||||||
|
from OCP.TopLoc import TopLoc_Location
|
||||||
|
from OCP.TopoDS import TopoDS, TopoDS_Shape
|
||||||
|
from OCP.TopTools import TopTools_IndexedMapOfShape, TopTools_ListOfShape
|
||||||
|
|
||||||
|
REL = 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def inc1(manifest):
|
||||||
|
if "inc1" not in manifest:
|
||||||
|
pytest.skip("manifest predates the Inc 1 reference block")
|
||||||
|
return manifest["inc1"]
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Helpers, deliberately mirroring tools/gen_fixtures.py
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def sub_shapes(shape, kind=None):
|
||||||
|
smap = TopTools_IndexedMapOfShape()
|
||||||
|
if kind is None:
|
||||||
|
TopExp.MapShapes_s(shape, smap)
|
||||||
|
else:
|
||||||
|
TopExp.MapShapes_s(shape, kind, smap)
|
||||||
|
return [smap.FindKey(i) for i in range(1, smap.Extent() + 1)]
|
||||||
|
|
||||||
|
|
||||||
|
def measure(shape) -> dict:
|
||||||
|
vol, area = GProp_GProps(), GProp_GProps()
|
||||||
|
BRepGProp.VolumeProperties_s(shape, vol)
|
||||||
|
BRepGProp.SurfaceProperties_s(shape, area)
|
||||||
|
box = Bnd_Box()
|
||||||
|
BRepBndLib.Add_s(shape, box)
|
||||||
|
return {
|
||||||
|
"volume": vol.Mass(),
|
||||||
|
"area": area.Mass(),
|
||||||
|
"bbox": None if box.IsVoid() else list(box.Get()),
|
||||||
|
"n_faces": len(sub_shapes(shape, TopAbs_FACE)),
|
||||||
|
"n_edges": len(sub_shapes(shape, TopAbs_EDGE)),
|
||||||
|
"n_solids": len(sub_shapes(shape, TopAbs_SOLID)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def history(builder, pre, post) -> list[dict]:
|
||||||
|
all_post = TopTools_IndexedMapOfShape()
|
||||||
|
TopExp.MapShapes_s(post, all_post)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"pre": i,
|
||||||
|
"deleted": bool(builder.IsDeleted(face)),
|
||||||
|
"modified": sorted(
|
||||||
|
all_post.FindIndex(s) - 1 for s in builder.Modified(face)
|
||||||
|
),
|
||||||
|
"generated": sorted(
|
||||||
|
all_post.FindIndex(s) - 1 for s in builder.Generated(face)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for i, face in enumerate(sub_shapes(pre, TopAbs_FACE))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def brep_sha(shape) -> str:
|
||||||
|
buf = io.BytesIO()
|
||||||
|
BinTools.Write_s(shape, buf)
|
||||||
|
return hashlib.sha256(buf.getvalue()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def linear(shape) -> float:
|
||||||
|
props = GProp_GProps()
|
||||||
|
BRepGProp.LinearProperties_s(shape, props)
|
||||||
|
return props.Mass()
|
||||||
|
|
||||||
|
|
||||||
|
def assert_close(actual, expected, what: str) -> None:
|
||||||
|
if expected is None:
|
||||||
|
assert actual is None, what
|
||||||
|
return
|
||||||
|
if isinstance(expected, list):
|
||||||
|
assert actual == pytest.approx(expected, rel=REL), what
|
||||||
|
else:
|
||||||
|
assert actual == pytest.approx(expected, rel=REL), what
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Measurement parity over the committed fixtures
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_measurements_match_stock(inc1, fixture_shapes):
|
||||||
|
for name, expected in inc1["measure"].items():
|
||||||
|
got = measure(fixture_shapes[name])
|
||||||
|
for key in ("n_faces", "n_edges", "n_solids"):
|
||||||
|
assert got[key] == expected[key], f"{name}.{key}"
|
||||||
|
for key in ("volume", "area", "bbox"):
|
||||||
|
assert_close(got[key], expected[key], f"{name}.{key}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_face_props_match_stock(inc1, fixture_shapes):
|
||||||
|
"""Per-face area and centroid, in MapShapes(FACE) order.
|
||||||
|
|
||||||
|
Also the reference n3xd_ocp.measure.face_surface_props has to reproduce:
|
||||||
|
the anchor path keys on this ordering.
|
||||||
|
"""
|
||||||
|
for name, expected in inc1["face_props"].items():
|
||||||
|
faces = sub_shapes(fixture_shapes[name], TopAbs_FACE)
|
||||||
|
assert len(faces) == len(expected), f"{name}: face count"
|
||||||
|
for i, (face, row) in enumerate(zip(faces, expected, strict=True)):
|
||||||
|
props = GProp_GProps()
|
||||||
|
BRepGProp.SurfaceProperties_s(face, props)
|
||||||
|
c = props.CentreOfMass()
|
||||||
|
assert_close(
|
||||||
|
[props.Mass(), c.X(), c.Y(), c.Z()], row, f"{name}.face[{i}]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_surface_properties_eps_overload_is_bound():
|
||||||
|
"""The 3-argument form exists and converges on the same answer for an
|
||||||
|
analytic face. The app's spline-area accuracy work needs this knob."""
|
||||||
|
box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
default, eps = GProp_GProps(), GProp_GProps()
|
||||||
|
BRepGProp.SurfaceProperties_s(box, default)
|
||||||
|
BRepGProp.SurfaceProperties_s(box, eps, 1e-6)
|
||||||
|
assert eps.Mass() == pytest.approx(default.Mass(), rel=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Boolean history — the topological-naming substrate
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _cut_cylinder_op():
|
||||||
|
op = BRepAlgoAPI_Cut()
|
||||||
|
base = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
tool = BRepPrimAPI_MakeCylinder(
|
||||||
|
gp_Ax2(gp_Pnt(5, 10, 0), gp_Dir(0, 0, 1)), 3.0, 30.0
|
||||||
|
).Shape()
|
||||||
|
args, tools = TopTools_ListOfShape(), TopTools_ListOfShape()
|
||||||
|
args.Append(base)
|
||||||
|
tools.Append(tool)
|
||||||
|
op.SetArguments(args)
|
||||||
|
op.SetTools(tools)
|
||||||
|
op.Build()
|
||||||
|
return op, base
|
||||||
|
|
||||||
|
|
||||||
|
def test_boolean_history_matches_stock_exactly(inc1):
|
||||||
|
op, base = _cut_cylinder_op()
|
||||||
|
assert op.IsDone()
|
||||||
|
assert history(op, base, op.Shape()) == inc1["cut_cylinder_history"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_of_an_untracked_subshape_raises_catchably():
|
||||||
|
"""The app wraps every history call in `except Exception`, because a builder
|
||||||
|
need not track an arbitrary sub-shape. Whatever OCCT does there must reach
|
||||||
|
Python as an exception, never as an abort."""
|
||||||
|
op, _base = _cut_cylinder_op()
|
||||||
|
stranger = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0).Shape()
|
||||||
|
try:
|
||||||
|
op.Modified(stranger)
|
||||||
|
op.Generated(stranger)
|
||||||
|
op.IsDeleted(stranger)
|
||||||
|
except Exception: # noqa: BLE001 — the point is that it is catchable
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_executing_boolean_constructors_are_not_bound():
|
||||||
|
"""The two-argument constructors execute during construction; the app's
|
||||||
|
call sites also called Build(), so the operation ran twice and the first,
|
||||||
|
destructive pass could mutate shared inputs. Refusing to bind them is what
|
||||||
|
makes that unrepresentable."""
|
||||||
|
a = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape()
|
||||||
|
b = BRepPrimAPI_MakeBox(5.0, 5.0, 5.0).Shape()
|
||||||
|
for cls in (BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
cls(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Constructors
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_arc_edge_matches_stock(inc1):
|
||||||
|
arc = GC_MakeArcOfCircle(
|
||||||
|
gp_Pnt(0, 0, 0), gp_Pnt(5, 5, 0), gp_Pnt(10, 0, 0)
|
||||||
|
).Value()
|
||||||
|
edge = BRepBuilderAPI_MakeEdge(arc).Edge()
|
||||||
|
expected = inc1["constructions"]["arc_edge"]
|
||||||
|
assert brep_sha(edge) == expected["sha256"]
|
||||||
|
assert_close(linear(edge), expected["length"], "arc length")
|
||||||
|
|
||||||
|
|
||||||
|
def test_bspline_edge_matches_stock(inc1):
|
||||||
|
"""A Python-constructed transient handed straight to the kernel.
|
||||||
|
|
||||||
|
This is the path that makes nb::init<> unusable for transients: the edge
|
||||||
|
builder stores a handle to the curve, so the curve must own its own heap
|
||||||
|
storage rather than live inside the Python instance.
|
||||||
|
"""
|
||||||
|
poles = TColgp_Array1OfPnt(1, 4)
|
||||||
|
for i, (x, y) in enumerate([(0, 0), (3, 6), (7, -4), (10, 2)], start=1):
|
||||||
|
poles.SetValue(i, gp_Pnt(float(x), float(y), 0.0))
|
||||||
|
knots = TColStd_Array1OfReal(1, 2)
|
||||||
|
knots.SetValue(1, 0.0)
|
||||||
|
knots.SetValue(2, 1.0)
|
||||||
|
mults = TColStd_Array1OfInteger(1, 2)
|
||||||
|
mults.SetValue(1, 4)
|
||||||
|
mults.SetValue(2, 4)
|
||||||
|
|
||||||
|
curve = Geom_BSplineCurve(poles, knots, mults, 3)
|
||||||
|
edge = BRepBuilderAPI_MakeEdge(curve).Edge()
|
||||||
|
del curve, poles, knots, mults # the edge must not depend on them
|
||||||
|
|
||||||
|
expected = inc1["constructions"]["spline_edge"]
|
||||||
|
assert brep_sha(edge) == expected["sha256"]
|
||||||
|
assert_close(linear(edge), expected["length"], "spline length")
|
||||||
|
|
||||||
|
|
||||||
|
def test_prism_matches_stock_including_history(inc1):
|
||||||
|
wire = BRepBuilderAPI_MakePolygon(
|
||||||
|
gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0), gp_Pnt(10, 6, 0), gp_Pnt(0, 6, 0), True
|
||||||
|
).Wire()
|
||||||
|
face = BRepBuilderAPI_MakeFace(wire, True).Face()
|
||||||
|
maker = BRepPrimAPI_MakePrism(face, gp_Vec(0.0, 0.0, 4.0))
|
||||||
|
prism = maker.Shape()
|
||||||
|
|
||||||
|
expected = inc1["constructions"]["prism"]
|
||||||
|
assert brep_sha(prism) == expected["sha256"]
|
||||||
|
got = measure(prism)
|
||||||
|
for key in ("n_faces", "n_edges", "n_solids"):
|
||||||
|
assert got[key] == expected[key], key
|
||||||
|
for key in ("volume", "area", "bbox"):
|
||||||
|
assert_close(got[key], expected[key], key)
|
||||||
|
# An extrude's provenance is read off exactly this map.
|
||||||
|
assert history(maker, face, prism) == expected["history"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_meshing_matches_stock_counts(inc1):
|
||||||
|
shape = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
BRepMesh_IncrementalMesh(shape, 0.1, False, 0.5, True)
|
||||||
|
|
||||||
|
got = []
|
||||||
|
for i, face in enumerate(sub_shapes(shape, TopAbs_FACE)):
|
||||||
|
loc = TopLoc_Location()
|
||||||
|
tri = BRep_Tool.Triangulation_s(TopoDS.Face_s(face), loc)
|
||||||
|
if tri is None:
|
||||||
|
continue # id gaps are preserved, exactly as tessellation.py does
|
||||||
|
got.append([i, tri.NbNodes(), tri.NbTriangles()])
|
||||||
|
assert got == inc1["box_meshed_counts"]
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Adaptors — the app's most-used query path
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_adaptor_reports_surface_kind_and_quadric():
|
||||||
|
"""`BRepAdaptor_Surface(f).Cylinder().Radius()` is the shape of nearly every
|
||||||
|
reference-inference call in the app, and it crosses three modules: the
|
||||||
|
virtuals live on Adaptor3d_Surface, the enum in GeomAbs, the result in gp.
|
||||||
|
"""
|
||||||
|
cyl = BRepPrimAPI_MakeCylinder(
|
||||||
|
gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 4.0, 12.0
|
||||||
|
).Shape()
|
||||||
|
|
||||||
|
kinds = {}
|
||||||
|
for face in sub_shapes(cyl, TopAbs_FACE):
|
||||||
|
adaptor = BRepAdaptor_Surface(TopoDS.Face_s(face))
|
||||||
|
kinds.setdefault(adaptor.GetType(), []).append(adaptor)
|
||||||
|
|
||||||
|
assert len(kinds[GeomAbs_Plane]) == 2
|
||||||
|
lateral = kinds[GeomAbs_Cylinder][0]
|
||||||
|
assert lateral.Cylinder().Radius() == pytest.approx(4.0)
|
||||||
|
assert lateral.Cylinder().Axis().Direction().Z() == pytest.approx(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_curve_adaptor_reports_line_and_bounds():
|
||||||
|
box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
edge = TopoDS.Edge_s(sub_shapes(box, TopAbs_EDGE)[0])
|
||||||
|
adaptor = BRepAdaptor_Curve(edge)
|
||||||
|
assert adaptor.GetType() == GeomAbs_Line
|
||||||
|
assert adaptor.LastParameter() > adaptor.FirstParameter()
|
||||||
|
direction = adaptor.Line().Direction()
|
||||||
|
assert abs(direction.X()) + abs(direction.Y()) + abs(direction.Z()) == (
|
||||||
|
pytest.approx(1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_surface_out_parameter_derivatives():
|
||||||
|
"""D1 keeps its out-parameters: the app pre-allocates and reads back."""
|
||||||
|
box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
face = TopoDS.Face_s(sub_shapes(box, TopAbs_FACE)[0])
|
||||||
|
adaptor = BRepAdaptor_Surface(face)
|
||||||
|
point, du, dv = gp_Pnt(), gp_Vec(), gp_Vec()
|
||||||
|
adaptor.D1(0.5, 0.5, point, du, dv)
|
||||||
|
assert du.Crossed(dv).Magnitude() > 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_brep_tool_surface_round_trips_through_geom():
|
||||||
|
"""Surface_s hands back a live Geom_Surface, which must survive the trip
|
||||||
|
back into the kernel — the projection is how feature recognition measures
|
||||||
|
tangency."""
|
||||||
|
from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf
|
||||||
|
|
||||||
|
box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
face = TopoDS.Face_s(sub_shapes(box, TopAbs_FACE)[0])
|
||||||
|
surf = BRep_Tool.Surface_s(face)
|
||||||
|
proj = GeomAPI_ProjectPointOnSurf(gp_Pnt(-5.0, 10.0, 15.0), surf)
|
||||||
|
assert proj.IsDone()
|
||||||
|
assert proj.NbPoints() >= 1
|
||||||
|
u, v = proj.LowerDistanceParameters()
|
||||||
|
assert isinstance(u, float) and isinstance(v, float)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bnd_box_get_returns_a_tuple():
|
||||||
|
box = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape()
|
||||||
|
bbox = Bnd_Box()
|
||||||
|
BRepBndLib.Add_s(box, bbox)
|
||||||
|
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
|
||||||
|
assert xmax - xmin == pytest.approx(10.0, abs=1e-6)
|
||||||
|
assert ymax - ymin == pytest.approx(20.0, abs=1e-6)
|
||||||
|
assert zmax - zmin == pytest.approx(30.0, abs=1e-6)
|
||||||
|
|
||||||
|
|
||||||
|
def test_history_survives_its_builder():
|
||||||
|
"""Sub-shapes come back by value, so a history list outlives the operator
|
||||||
|
that produced it — the app keeps provenance around long after."""
|
||||||
|
op, base = _cut_cylinder_op()
|
||||||
|
pierced = sub_shapes(base, TopAbs_FACE)[4]
|
||||||
|
modified = list(op.Modified(pierced))
|
||||||
|
del op
|
||||||
|
assert modified
|
||||||
|
assert all(isinstance(s, TopoDS_Shape) and not s.IsNull() for s in modified)
|
||||||
@@ -17,6 +17,13 @@ Two properties are recorded, and the first is checked here rather than assumed:
|
|||||||
|
|
||||||
Face counts are recorded alongside, pinning the map ordering that face and edge
|
Face counts are recorded alongside, pinning the map ordering that face and edge
|
||||||
identity depend on throughout the topology code.
|
identity depend on throughout the topology code.
|
||||||
|
|
||||||
|
From Inc 1 on, the manifest also carries a per-increment reference block, since
|
||||||
|
the app's own tests cannot gate an increment: backend/tests/conftest.py imports
|
||||||
|
n3xd.main, so every one of them fails at collection until the last module is
|
||||||
|
bound. What the blocks record is chosen to be robust: counts and boolean
|
||||||
|
history maps are topological and compare exactly, measurements compare at a
|
||||||
|
relative tolerance. See docs/adding-symbols.md.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -67,20 +74,27 @@ def _fused() -> TopoDS_Shape:
|
|||||||
return op.Shape()
|
return op.Shape()
|
||||||
|
|
||||||
|
|
||||||
def _cut_cylinder() -> TopoDS_Shape:
|
def _cut_cylinder_op():
|
||||||
op = BRepAlgoAPI_Cut()
|
"""The deferred cut, returned unbuilt operands and all, so the Inc 1 block
|
||||||
|
can read its history against the same operand shapes."""
|
||||||
from OCP.TopTools import TopTools_ListOfShape
|
from OCP.TopTools import TopTools_ListOfShape
|
||||||
|
|
||||||
args, tools = TopTools_ListOfShape(), TopTools_ListOfShape()
|
op = BRepAlgoAPI_Cut()
|
||||||
args.Append(_box())
|
base = _box()
|
||||||
tools.Append(
|
tool = BRepPrimAPI_MakeCylinder(
|
||||||
BRepPrimAPI_MakeCylinder(
|
|
||||||
gp_Ax2(gp_Pnt(5, 10, 0), gp_Dir(0, 0, 1)), 3.0, 30.0
|
gp_Ax2(gp_Pnt(5, 10, 0), gp_Dir(0, 0, 1)), 3.0, 30.0
|
||||||
).Shape()
|
).Shape()
|
||||||
)
|
args, tools = TopTools_ListOfShape(), TopTools_ListOfShape()
|
||||||
|
args.Append(base)
|
||||||
|
tools.Append(tool)
|
||||||
op.SetArguments(args)
|
op.SetArguments(args)
|
||||||
op.SetTools(tools)
|
op.SetTools(tools)
|
||||||
op.Build()
|
op.Build()
|
||||||
|
return op, base, tool
|
||||||
|
|
||||||
|
|
||||||
|
def _cut_cylinder() -> TopoDS_Shape:
|
||||||
|
op, _base, _tool = _cut_cylinder_op()
|
||||||
return op.Shape()
|
return op.Shape()
|
||||||
|
|
||||||
|
|
||||||
@@ -134,6 +148,187 @@ def face_count(shape: TopoDS_Shape) -> int:
|
|||||||
return faces.Extent()
|
return faces.Extent()
|
||||||
|
|
||||||
|
|
||||||
|
def _sub_shapes(shape: TopoDS_Shape, kind):
|
||||||
|
smap = TopTools_IndexedMapOfShape()
|
||||||
|
TopExp.MapShapes_s(shape, kind, smap)
|
||||||
|
return [smap.FindKey(i) for i in range(1, smap.Extent() + 1)]
|
||||||
|
|
||||||
|
|
||||||
|
def _measure(shape: TopoDS_Shape) -> dict:
|
||||||
|
"""Volume, area and bbox, all through the default (non-eps) quadrature."""
|
||||||
|
from OCP.Bnd import Bnd_Box
|
||||||
|
from OCP.BRepBndLib import BRepBndLib
|
||||||
|
from OCP.BRepGProp import BRepGProp
|
||||||
|
from OCP.GProp import GProp_GProps
|
||||||
|
from OCP.TopAbs import TopAbs_EDGE, TopAbs_SOLID
|
||||||
|
|
||||||
|
vol, area = GProp_GProps(), GProp_GProps()
|
||||||
|
BRepGProp.VolumeProperties_s(shape, vol)
|
||||||
|
BRepGProp.SurfaceProperties_s(shape, area)
|
||||||
|
|
||||||
|
box = Bnd_Box()
|
||||||
|
BRepBndLib.Add_s(shape, box)
|
||||||
|
bbox = None if box.IsVoid() else list(box.Get())
|
||||||
|
|
||||||
|
return {
|
||||||
|
"volume": vol.Mass(),
|
||||||
|
"area": area.Mass(),
|
||||||
|
"bbox": bbox,
|
||||||
|
"n_faces": face_count(shape),
|
||||||
|
"n_edges": len(_sub_shapes(shape, TopAbs_EDGE)),
|
||||||
|
"n_solids": len(_sub_shapes(shape, TopAbs_SOLID)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _face_props(shape: TopoDS_Shape) -> list[list[float]]:
|
||||||
|
"""Per-face [area, cx, cy, cz] in MapShapes(FACE) order.
|
||||||
|
|
||||||
|
This is the reference for n3xd_ocp.measure.face_surface_props, and the
|
||||||
|
ordering contract the whole anchor path depends on.
|
||||||
|
"""
|
||||||
|
from OCP.BRepGProp import BRepGProp
|
||||||
|
from OCP.GProp import GProp_GProps
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for face in _sub_shapes(shape, TopAbs_FACE):
|
||||||
|
props = GProp_GProps()
|
||||||
|
BRepGProp.SurfaceProperties_s(face, props)
|
||||||
|
c = props.CentreOfMass()
|
||||||
|
out.append([props.Mass(), c.X(), c.Y(), c.Z()])
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _history(builder, pre: TopoDS_Shape, post: TopoDS_Shape) -> list[dict]:
|
||||||
|
"""The Modified/Generated/IsDeleted map, keyed by pre-shape face index.
|
||||||
|
|
||||||
|
Recorded as *post-shape sub-shape indices* rather than shapes, so the
|
||||||
|
comparison is purely topological — this is the substrate the app's
|
||||||
|
topological naming is built on, and it must not drift by a single entry.
|
||||||
|
Indices come from the untyped MapShapes, because a builder may generate a
|
||||||
|
shape of any type (MakePrism answers a face with the solid it swept).
|
||||||
|
"""
|
||||||
|
all_post = TopTools_IndexedMapOfShape()
|
||||||
|
TopExp.MapShapes_s(post, all_post)
|
||||||
|
|
||||||
|
def index_of(shape: TopoDS_Shape) -> int:
|
||||||
|
return all_post.FindIndex(shape) - 1 # -1 when absent, else 0-based
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for i, face in enumerate(_sub_shapes(pre, TopAbs_FACE)):
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"pre": i,
|
||||||
|
"deleted": bool(builder.IsDeleted(face)),
|
||||||
|
"modified": sorted(index_of(s) for s in builder.Modified(face)),
|
||||||
|
"generated": sorted(index_of(s) for s in builder.Generated(face)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def _mesh_counts(shape: TopoDS_Shape) -> list[list[int]]:
|
||||||
|
"""Per-face [nb_nodes, nb_triangles], skipping faces with no triangulation.
|
||||||
|
|
||||||
|
Mirrors what cad/tessellation.py extracts, including the "faces without a
|
||||||
|
mesh leave a gap in the id sequence" rule.
|
||||||
|
"""
|
||||||
|
from OCP.BRep import BRep_Tool
|
||||||
|
from OCP.TopoDS import TopoDS
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for i, face in enumerate(_sub_shapes(shape, TopAbs_FACE)):
|
||||||
|
loc = TopLoc_Location()
|
||||||
|
tri = BRep_Tool.Triangulation_s(TopoDS.Face_s(face), loc)
|
||||||
|
if tri is None:
|
||||||
|
continue
|
||||||
|
out.append([i, tri.NbNodes(), tri.NbTriangles()])
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _constructions() -> dict:
|
||||||
|
"""Small shapes built through the Inc 1 constructors the app uses.
|
||||||
|
|
||||||
|
Recorded by BREP digest plus a measurement, so the test can rebuild them
|
||||||
|
and compare both the bytes and the geometry.
|
||||||
|
"""
|
||||||
|
from OCP.BRepBuilderAPI import (
|
||||||
|
BRepBuilderAPI_MakeEdge,
|
||||||
|
BRepBuilderAPI_MakeFace,
|
||||||
|
BRepBuilderAPI_MakePolygon,
|
||||||
|
)
|
||||||
|
from OCP.BRepGProp import BRepGProp
|
||||||
|
from OCP.BRepPrimAPI import BRepPrimAPI_MakePrism
|
||||||
|
from OCP.GC import GC_MakeArcOfCircle
|
||||||
|
from OCP.Geom import Geom_BSplineCurve
|
||||||
|
from OCP.GProp import GProp_GProps
|
||||||
|
from OCP.TColgp import TColgp_Array1OfPnt
|
||||||
|
from OCP.TColStd import TColStd_Array1OfInteger, TColStd_Array1OfReal
|
||||||
|
|
||||||
|
def length(shape: TopoDS_Shape) -> float:
|
||||||
|
props = GProp_GProps()
|
||||||
|
BRepGProp.LinearProperties_s(shape, props)
|
||||||
|
return props.Mass()
|
||||||
|
|
||||||
|
arc = GC_MakeArcOfCircle(
|
||||||
|
gp_Pnt(0, 0, 0), gp_Pnt(5, 5, 0), gp_Pnt(10, 0, 0)
|
||||||
|
).Value()
|
||||||
|
arc_edge = BRepBuilderAPI_MakeEdge(arc).Edge()
|
||||||
|
|
||||||
|
# A clamped cubic through four poles — the shape sketch_builder/edges.py
|
||||||
|
# builds for a spline element.
|
||||||
|
poles = TColgp_Array1OfPnt(1, 4)
|
||||||
|
for i, (x, y) in enumerate([(0, 0), (3, 6), (7, -4), (10, 2)], start=1):
|
||||||
|
poles.SetValue(i, gp_Pnt(float(x), float(y), 0.0))
|
||||||
|
knots = TColStd_Array1OfReal(1, 2)
|
||||||
|
knots.SetValue(1, 0.0)
|
||||||
|
knots.SetValue(2, 1.0)
|
||||||
|
mults = TColStd_Array1OfInteger(1, 2)
|
||||||
|
mults.SetValue(1, 4)
|
||||||
|
mults.SetValue(2, 4)
|
||||||
|
spline_edge = BRepBuilderAPI_MakeEdge(
|
||||||
|
Geom_BSplineCurve(poles, knots, mults, 3)
|
||||||
|
).Edge()
|
||||||
|
|
||||||
|
poly = BRepBuilderAPI_MakePolygon(
|
||||||
|
gp_Pnt(0, 0, 0), gp_Pnt(10, 0, 0), gp_Pnt(10, 6, 0), gp_Pnt(0, 6, 0), True
|
||||||
|
).Wire()
|
||||||
|
prism_face = BRepBuilderAPI_MakeFace(poly, True).Face()
|
||||||
|
prism_maker = BRepPrimAPI_MakePrism(prism_face, gp_Vec(0.0, 0.0, 4.0))
|
||||||
|
prism = prism_maker.Shape()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"arc_edge": {
|
||||||
|
"sha256": hashlib.sha256(write_bytes(arc_edge)).hexdigest(),
|
||||||
|
"length": length(arc_edge),
|
||||||
|
},
|
||||||
|
"spline_edge": {
|
||||||
|
"sha256": hashlib.sha256(write_bytes(spline_edge)).hexdigest(),
|
||||||
|
"length": length(spline_edge),
|
||||||
|
},
|
||||||
|
"prism": {
|
||||||
|
"sha256": hashlib.sha256(write_bytes(prism)).hexdigest(),
|
||||||
|
**_measure(prism),
|
||||||
|
# The prism's history is what an extrude's provenance reads.
|
||||||
|
"history": _history(prism_maker, prism_face, prism),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _inc1_reference() -> dict:
|
||||||
|
op, base, _tool = _cut_cylinder_op()
|
||||||
|
return {
|
||||||
|
"measure": {name: _measure(build()) for name, build in SHAPES.items()},
|
||||||
|
"face_props": {
|
||||||
|
name: _face_props(build())
|
||||||
|
for name, build in SHAPES.items()
|
||||||
|
if name != "empty_compound"
|
||||||
|
},
|
||||||
|
"cut_cylinder_history": _history(op, base, op.Shape()),
|
||||||
|
"box_meshed_counts": _mesh_counts(_meshed_box()),
|
||||||
|
"constructions": _constructions(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
import OCP
|
import OCP
|
||||||
|
|
||||||
@@ -163,6 +358,8 @@ def main() -> int:
|
|||||||
}
|
}
|
||||||
print(f"{name}: {len(data)} bytes, {manifest['shapes'][name]['faces']} faces")
|
print(f"{name}: {len(data)} bytes, {manifest['shapes'][name]['faces']} faces")
|
||||||
|
|
||||||
|
manifest["inc1"] = _inc1_reference()
|
||||||
|
|
||||||
(OUT / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
(OUT / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
||||||
print(f"\nwrote {len(SHAPES)} fixtures + manifest to {OUT}")
|
print(f"\nwrote {len(SHAPES)} fixtures + manifest to {OUT}")
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
156
tools/sigdiff.py
Normal file
156
tools/sigdiff.py
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
"""Compare this binding's bound surface against the stock wheel's.
|
||||||
|
|
||||||
|
`inventory.py --check` answers "does the symbol exist"; this answers "does it
|
||||||
|
mean the same thing". The gap between those two is where a binding does real
|
||||||
|
damage: an `nb::init<...>` whose types differ from the constructor you meant
|
||||||
|
can bind a *different overload* through an implicit conversion and build valid
|
||||||
|
but wrong geometry, silently. That happened once — BRepPrimAPI_MakePrism's
|
||||||
|
gp_Vec form resolving to the semi-infinite gp_Dir one, because gp_Dir converts
|
||||||
|
from gp_Vec — and the fixture digests are what caught it. This finds the class
|
||||||
|
of bug directly.
|
||||||
|
|
||||||
|
Two runs, then a diff:
|
||||||
|
|
||||||
|
# under the stock wheel, from the app checkout
|
||||||
|
uv run --project backend python ../ocp/tools/sigdiff.py --dump stock.json
|
||||||
|
# under ours
|
||||||
|
make shell # then: /cache/venv/bin/python /io/tools/sigdiff.py --dump /io/ours.json
|
||||||
|
python tools/sigdiff.py --compare stock.json ours.json
|
||||||
|
|
||||||
|
The comparison is one-sided on purpose: binding *fewer* overloads than stock is
|
||||||
|
the normal state of this project, so only surface we expose that stock does not
|
||||||
|
is reported. pybind11 spells the receiver as `self: Cls` and nanobind as plain
|
||||||
|
`self`, so the leading class name is dropped before comparing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
HERE = pathlib.Path(__file__).resolve().parent
|
||||||
|
DEFAULT_INVENTORY = HERE.parent / "inventory.json"
|
||||||
|
|
||||||
|
#: Parameter annotations, as both wrappers write them: `name: Some.Type`.
|
||||||
|
_ANNOTATION = re.compile(r":\s*([A-Za-z_][\w.]*)")
|
||||||
|
|
||||||
|
|
||||||
|
def _signatures(owner, name: str) -> list[tuple[str, ...]]:
|
||||||
|
"""Every overload of `name`, reduced to its parameter type names."""
|
||||||
|
doc = getattr(getattr(owner, name, None), "__doc__", None)
|
||||||
|
if not doc:
|
||||||
|
return []
|
||||||
|
out = set()
|
||||||
|
for line in doc.splitlines():
|
||||||
|
line = re.sub(r"^\d+\.\s*", "", line.strip())
|
||||||
|
if "(" not in line or ")" not in line:
|
||||||
|
continue
|
||||||
|
inner = line[line.index("(") + 1 : line.rindex(")")]
|
||||||
|
types = tuple(t.split(".")[-1] for t in _ANNOTATION.findall(inner))
|
||||||
|
out.add(types)
|
||||||
|
return sorted(out)
|
||||||
|
|
||||||
|
|
||||||
|
def dump(inventory: pathlib.Path, out: pathlib.Path) -> int:
|
||||||
|
data = json.loads(inventory.read_text())
|
||||||
|
result: dict = {}
|
||||||
|
|
||||||
|
for mod, symbols in data["modules"].items():
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(f"OCP.{mod}")
|
||||||
|
except ImportError:
|
||||||
|
continue
|
||||||
|
for name in symbols:
|
||||||
|
obj = getattr(module, name, None)
|
||||||
|
if obj is None:
|
||||||
|
continue
|
||||||
|
# OCCT namespaces (TopoDS, BRepGProp, ...) have no class to hang
|
||||||
|
# their statics on: upstream exposes them as a module, this binding
|
||||||
|
# as an empty carrier class. Both answer `TopoDS.Face_s(...)`, so
|
||||||
|
# compare their members either way.
|
||||||
|
if not isinstance(obj, (type, types.ModuleType)):
|
||||||
|
continue
|
||||||
|
# An nb::is_arithmetic() enum derives from int, so dir() carries
|
||||||
|
# int's methods; pybind11's enum does not. Not surface we bound.
|
||||||
|
inherited = set(dir(int)) if isinstance(obj, type) and issubclass(
|
||||||
|
obj, int) else set()
|
||||||
|
result[f"{mod}.{name}"] = {
|
||||||
|
"init": _signatures(obj, "__init__"),
|
||||||
|
"members": sorted(
|
||||||
|
m for m in dir(obj)
|
||||||
|
if not m.startswith("_") and m not in inherited
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write_text(json.dumps(result, indent=1) + "\n")
|
||||||
|
print(f"{len(result)} classes -> {out}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def compare(stock_path: pathlib.Path, ours_path: pathlib.Path) -> int:
|
||||||
|
stock = json.loads(stock_path.read_text())
|
||||||
|
ours = json.loads(ours_path.read_text())
|
||||||
|
problems = 0
|
||||||
|
|
||||||
|
def normalized(entry: dict, cls: str) -> set[tuple[str, ...]]:
|
||||||
|
out = set()
|
||||||
|
for sig in entry.get("init", []):
|
||||||
|
sig = list(sig)
|
||||||
|
if sig and sig[0] == cls: # pybind11's `self: Cls`
|
||||||
|
sig = sig[1:]
|
||||||
|
out.add(tuple(sig))
|
||||||
|
return out
|
||||||
|
|
||||||
|
for key, mine in sorted(ours.items()):
|
||||||
|
theirs = stock.get(key)
|
||||||
|
if theirs is None:
|
||||||
|
print(f"{key}: not present in the stock wheel")
|
||||||
|
problems += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
cls = key.split(".", 1)[1]
|
||||||
|
extra_init = normalized(mine, cls) - normalized(theirs, cls)
|
||||||
|
if extra_init:
|
||||||
|
problems += 1
|
||||||
|
print(f"{key}: constructor overloads stock does not have")
|
||||||
|
for sig in sorted(extra_init):
|
||||||
|
print(f" ours : ({', '.join(sig)})")
|
||||||
|
for sig in sorted(normalized(theirs, cls)):
|
||||||
|
print(f" stock: ({', '.join(sig)})")
|
||||||
|
|
||||||
|
extra_members = set(mine["members"]) - set(theirs["members"])
|
||||||
|
if extra_members:
|
||||||
|
problems += 1
|
||||||
|
print(f"{key}: members stock does not have: {sorted(extra_members)}")
|
||||||
|
|
||||||
|
if problems:
|
||||||
|
print(f"\n{problems} difference(s) — each is either a mis-bound overload "
|
||||||
|
f"or a deliberate deviation worth a comment in its module")
|
||||||
|
return 1
|
||||||
|
print(f"{len(ours)} classes, no surface we expose that stock does not")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
|
ap.add_argument("--dump", type=pathlib.Path, help="write this wheel's surface")
|
||||||
|
ap.add_argument("--compare", nargs=2, type=pathlib.Path,
|
||||||
|
metavar=("STOCK", "OURS"))
|
||||||
|
ap.add_argument("--inventory", type=pathlib.Path, default=DEFAULT_INVENTORY)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if args.dump:
|
||||||
|
return dump(args.inventory, args.dump)
|
||||||
|
if args.compare:
|
||||||
|
return compare(*args.compare)
|
||||||
|
ap.print_help()
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user