10C Inc 2: feature tail

119 of the app's 138 symbols now resolve; only the I/O modules and the Inc 4
tail are left. New: GCE2d, GCPnts, BRepFilletAPI, BRepOffsetAPI,
ShapeAnalysis, ShapeFix, ShapeUpgrade, BRepCheck, BRepTools, BRepLib,
BRepExtrema, BRepClass3d, IntCurvesFace, plus TopTools_HSequenceOfShape.

The fillet and chamfer builders derive BRepBuilderAPI_MakeShape, so their
history comes from the base bound in Inc 1 — which is what lets the app read a
blend's provenance the same way it reads a boolean's. The Inc 2 gate compares
that history exactly, alongside the splitter's, which is what sketch-region
attribution depends on.

Two shapes of deviation, both commented where they are bound:

- Trailing enum arguments are left off four constructors (fillet's
  ChFi3d_FilletShape, MakeThickSolidByJoin's mode/join pair, BRepExtrema's
  Extrema flags, and Inc 1's GeomAPI_ProjectPointOnSurf). An unregistered enum
  cannot serve as a default argument — nanobind converts defaults at binding
  time, so it fails the whole module's import with std::bad_cast. The app never
  overrides any of them, so OCCT's own defaults apply and behaviour is
  unchanged.
- ShapeAnalysis_FreeBounds.ConnectEdgesToWires reassigns a Handle& out
  parameter, which the caster cannot honour because it hands C++ a copy of the
  handle. The lambda splices the result into the sequence the caller passed, so
  the pass-empty-then-read shape the app uses still works. This is the one
  place upstream's holder semantics and ours differ observably.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfriM8XUkn7uYf5Dwe2xo6
This commit is contained in:
2026-08-10 20:09:29 +02:00
parent 340315b6e9
commit 0ce43a94aa
20 changed files with 1261 additions and 1 deletions

View File

@@ -52,6 +52,20 @@ void register_BRepAlgoAPI(nb::module_ &);
void register_BRepPrimAPI(nb::module_ &);
void register_GC(nb::module_ &);
void register_BRepMesh(nb::module_ &);
// Inc 2 — feature tail
void register_GCE2d(nb::module_ &);
void register_GCPnts(nb::module_ &);
void register_BRepFilletAPI(nb::module_ &);
void register_BRepOffsetAPI(nb::module_ &);
void register_ShapeAnalysis(nb::module_ &);
void register_ShapeFix(nb::module_ &);
void register_ShapeUpgrade(nb::module_ &);
void register_BRepCheck(nb::module_ &);
void register_BRepTools(nb::module_ &);
void register_BRepLib(nb::module_ &);
void register_BRepExtrema(nb::module_ &);
void register_BRepClass3d(nb::module_ &);
void register_IntCurvesFace(nb::module_ &);
void register_ext(nb::module_ &);
NB_MODULE(_OCP, m) {
@@ -95,6 +109,21 @@ NB_MODULE(_OCP, m) {
register_GC(m);
register_BRepMesh(m);
// Inc 2.
register_GCE2d(m);
register_GCPnts(m);
register_BRepFilletAPI(m);
register_BRepOffsetAPI(m);
register_ShapeAnalysis(m);
register_ShapeFix(m);
register_ShapeUpgrade(m);
register_BRepCheck(m);
register_BRepTools(m);
register_BRepLib(m);
register_BRepExtrema(m);
register_BRepClass3d(m);
register_IntCurvesFace(m);
register_ext(m);
m.attr("__all_modules__") = ocp_module_registry();

View File

@@ -0,0 +1,89 @@
/*
OCP.BRepCheck — shape validity.
The analyzer runs on every preview and after every boolean. Its
constructor does the work, so that is what releases the GIL; the per-face
Result() lookups afterwards are cheap.
Only the two status values the app tests for are worth calling out —
it names the self-intersecting/intersecting-wire pair when deciding whether
an imported shape is repairable — but the whole enum is bound, since it is
what Result() reports through.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepCheck_Analyzer.hxx>
#include <BRepCheck_Result.hxx>
#include <BRepCheck_Status.hxx>
#include <TopoDS_Shape.hxx>
void register_BRepCheck(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepCheck");
nb::enum_<BRepCheck_Status>(m, "BRepCheck_Status", nb::is_arithmetic())
.value("BRepCheck_NoError", BRepCheck_NoError)
.value("BRepCheck_InvalidPointOnCurve", BRepCheck_InvalidPointOnCurve)
.value("BRepCheck_InvalidPointOnCurveOnSurface",
BRepCheck_InvalidPointOnCurveOnSurface)
.value("BRepCheck_InvalidPointOnSurface", BRepCheck_InvalidPointOnSurface)
.value("BRepCheck_No3DCurve", BRepCheck_No3DCurve)
.value("BRepCheck_Multiple3DCurve", BRepCheck_Multiple3DCurve)
.value("BRepCheck_Invalid3DCurve", BRepCheck_Invalid3DCurve)
.value("BRepCheck_NoCurveOnSurface", BRepCheck_NoCurveOnSurface)
.value("BRepCheck_InvalidCurveOnSurface", BRepCheck_InvalidCurveOnSurface)
.value("BRepCheck_InvalidCurveOnClosedSurface",
BRepCheck_InvalidCurveOnClosedSurface)
.value("BRepCheck_InvalidSameRangeFlag", BRepCheck_InvalidSameRangeFlag)
.value("BRepCheck_InvalidSameParameterFlag",
BRepCheck_InvalidSameParameterFlag)
.value("BRepCheck_InvalidDegeneratedFlag", BRepCheck_InvalidDegeneratedFlag)
.value("BRepCheck_FreeEdge", BRepCheck_FreeEdge)
.value("BRepCheck_InvalidMultiConnexity", BRepCheck_InvalidMultiConnexity)
.value("BRepCheck_InvalidRange", BRepCheck_InvalidRange)
.value("BRepCheck_EmptyWire", BRepCheck_EmptyWire)
.value("BRepCheck_RedundantEdge", BRepCheck_RedundantEdge)
.value("BRepCheck_SelfIntersectingWire", BRepCheck_SelfIntersectingWire)
.value("BRepCheck_NoSurface", BRepCheck_NoSurface)
.value("BRepCheck_InvalidWire", BRepCheck_InvalidWire)
.value("BRepCheck_RedundantWire", BRepCheck_RedundantWire)
.value("BRepCheck_IntersectingWires", BRepCheck_IntersectingWires)
.value("BRepCheck_InvalidImbricationOfWires",
BRepCheck_InvalidImbricationOfWires)
.value("BRepCheck_EmptyShell", BRepCheck_EmptyShell)
.value("BRepCheck_RedundantFace", BRepCheck_RedundantFace)
.value("BRepCheck_InvalidImbricationOfShells",
BRepCheck_InvalidImbricationOfShells)
.value("BRepCheck_UnorientableShape", BRepCheck_UnorientableShape)
.value("BRepCheck_NotClosed", BRepCheck_NotClosed)
.value("BRepCheck_NotConnected", BRepCheck_NotConnected)
.value("BRepCheck_SubshapeNotInShape", BRepCheck_SubshapeNotInShape)
.value("BRepCheck_BadOrientation", BRepCheck_BadOrientation)
.value("BRepCheck_BadOrientationOfSubshape",
BRepCheck_BadOrientationOfSubshape)
.value("BRepCheck_InvalidPolygonOnTriangulation",
BRepCheck_InvalidPolygonOnTriangulation)
.value("BRepCheck_InvalidToleranceValue", BRepCheck_InvalidToleranceValue)
.value("BRepCheck_EnclosedRegion", BRepCheck_EnclosedRegion)
.value("BRepCheck_CheckFail", BRepCheck_CheckFail)
.export_values();
nb::class_<BRepCheck_Result, Standard_Transient>(m, "BRepCheck_Result")
.def("Status", &BRepCheck_Result::Status, OCP_RETURN_COPY)
.def("IsMinimum", &BRepCheck_Result::IsMinimum);
nb::class_<BRepCheck_Analyzer>(m, "BRepCheck_Analyzer")
.def(nb::init<const TopoDS_Shape &, const Standard_Boolean,
const Standard_Boolean, const Standard_Boolean>(),
"S"_a, "GeomControls"_a = Standard_True,
"theIsParallel"_a = Standard_False,
"theIsExact"_a = Standard_False, OCP_NOGIL)
.def("IsValid", nb::overload_cast<>(&BRepCheck_Analyzer::IsValid,
nb::const_))
.def("IsValid",
nb::overload_cast<const TopoDS_Shape &>(&BRepCheck_Analyzer::IsValid,
nb::const_),
"S"_a)
.def("Result", &BRepCheck_Analyzer::Result, "theSubS"_a);
}

View File

@@ -0,0 +1,31 @@
/*
OCP.BRepClass3d — point-in-solid classification.
Answers "is this point inside the material", which the app uses to decide
which side of a face to grow a feature from and to verify that a thread
groove did not gouge its shoulder. The three-argument constructor
classifies immediately; the default one needs a separate Perform.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepClass3d_SolidClassifier.hxx>
#include <TopoDS_Shape.hxx>
#include <gp_Pnt.hxx>
void register_BRepClass3d(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepClass3d");
nb::class_<BRepClass3d_SolidClassifier>(m, "BRepClass3d_SolidClassifier")
.def(nb::init<>())
.def(nb::init<const TopoDS_Shape &>(), "S"_a, OCP_NOGIL)
.def(nb::init<const TopoDS_Shape &, const gp_Pnt &,
const Standard_Real>(),
"S"_a, "P"_a, "Tol"_a, OCP_NOGIL)
.def("Load", &BRepClass3d_SolidClassifier::Load, "S"_a, OCP_NOGIL)
.def("Perform", &BRepClass3d_SolidClassifier::Perform, "P"_a, "Tol"_a,
OCP_NOGIL)
.def("State", &BRepClass3d_SolidClassifier::State)
.def("IsOnAFace", &BRepClass3d_SolidClassifier::IsOnAFace);
}

View File

@@ -0,0 +1,34 @@
/*
OCP.BRepExtrema — minimum distance between two shapes.
Used by the measurement tools and the interference check. The constructor
performs the search, so it releases the GIL.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepExtrema_DistShapeShape.hxx>
#include <TopoDS_Shape.hxx>
#include <gp_Pnt.hxx>
void register_BRepExtrema(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepExtrema");
nb::class_<BRepExtrema_DistShapeShape>(m, "BRepExtrema_DistShapeShape")
.def(nb::init<>())
// Without the Extrema flag/algo overload: unregistered enums cannot
// serve as default arguments, and the app takes OCCT's defaults.
.def(nb::init<const TopoDS_Shape &, const TopoDS_Shape &>(), "Shape1"_a,
"Shape2"_a, OCP_NOGIL)
.def(
"Perform", [](BRepExtrema_DistShapeShape &self) { self.Perform(); },
OCP_NOGIL)
.def("IsDone", &BRepExtrema_DistShapeShape::IsDone)
.def("NbSolution", &BRepExtrema_DistShapeShape::NbSolution)
.def("Value", &BRepExtrema_DistShapeShape::Value)
.def("PointOnShape1", &BRepExtrema_DistShapeShape::PointOnShape1, "N"_a,
OCP_RETURN_COPY)
.def("PointOnShape2", &BRepExtrema_DistShapeShape::PointOnShape2, "N"_a,
OCP_RETURN_COPY);
}

View File

@@ -0,0 +1,66 @@
/*
OCP.BRepFilletAPI — fillets and chamfers.
Both derive BRepBuilderAPI_MakeShape, so Build/Shape and — importantly —
the Generated/Modified/IsDeleted history come from there. The app reads
that history to attribute the blend faces back to the feature that made
them, the same way it reads a boolean's.
These are the two operations the app runs in an isolated subprocess, since
OCCT can hard-fault on a degenerate blend; nothing here changes that, but
releasing the GIL on Build() is what lets the pool overlap work.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepBuilderAPI_MakeShape.hxx>
#include <BRepFilletAPI_MakeChamfer.hxx>
#include <BRepFilletAPI_MakeFillet.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
void register_BRepFilletAPI(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepFilletAPI");
nb::class_<BRepFilletAPI_MakeFillet, BRepBuilderAPI_MakeShape>(
m, "BRepFilletAPI_MakeFillet")
// The ChFi3d_FilletShape overload is left out: it exists only to
// carry a default the app never overrides, and binding an unregistered
// enum as a default argument fails at module-init time.
.def(nb::init<const TopoDS_Shape &>(), "S"_a)
.def("Add",
nb::overload_cast<const Standard_Real, const TopoDS_Edge &>(
&BRepFilletAPI_MakeFillet::Add),
"Radius"_a, "E"_a)
.def("Add",
nb::overload_cast<const Standard_Real, const Standard_Real,
const TopoDS_Edge &>(
&BRepFilletAPI_MakeFillet::Add),
"R1"_a, "R2"_a, "E"_a)
.def("NbContours", &BRepFilletAPI_MakeFillet::NbContours)
.def("Reset", &BRepFilletAPI_MakeFillet::Reset);
nb::class_<BRepFilletAPI_MakeChamfer, BRepBuilderAPI_MakeShape>(
m, "BRepFilletAPI_MakeChamfer")
.def(nb::init<const TopoDS_Shape &>(), "S"_a)
.def("Add",
nb::overload_cast<const TopoDS_Edge &>(
&BRepFilletAPI_MakeChamfer::Add),
"E"_a)
.def("Add",
nb::overload_cast<const Standard_Real, const TopoDS_Edge &>(
&BRepFilletAPI_MakeChamfer::Add),
"Dis"_a, "E"_a)
.def("Add",
nb::overload_cast<const Standard_Real, const Standard_Real,
const TopoDS_Edge &, const TopoDS_Face &>(
&BRepFilletAPI_MakeChamfer::Add),
"Dis1"_a, "Dis2"_a, "E"_a, "F"_a)
// Distance-and-angle, which is how the app words an asymmetric chamfer.
.def("AddDA", &BRepFilletAPI_MakeChamfer::AddDA, "Dis"_a, "Angle"_a,
"E"_a, "F"_a)
.def("NbContours", &BRepFilletAPI_MakeChamfer::NbContours)
.def("Reset", &BRepFilletAPI_MakeChamfer::Reset);
}

View File

@@ -0,0 +1,31 @@
/*
OCP.BRepLib — repairs applied to shapes built edge by edge.
BuildCurves3d recovers the 3D curve of an edge that only carries a pcurve,
which is how the thread's helical edges (laid out in the cylinder's UV
space) become real geometry. OrientClosedSolid fixes a solid built from a
sewn shell whose orientation came out inward.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepLib.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Solid.hxx>
void register_BRepLib(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepLib");
nb::class_<BRepLib> cls(m, "BRepLib");
OCP_DEF_S(
cls, "BuildCurves3d",
[](const TopoDS_Shape &S) { return BRepLib::BuildCurves3d(S); }, "S"_a,
OCP_NOGIL);
OCP_DEF_S(
cls, "OrientClosedSolid",
[](TopoDS_Solid &solid) { return BRepLib::OrientClosedSolid(solid); },
"solid"_a, OCP_NOGIL);
}

View File

@@ -0,0 +1,86 @@
/*
OCP.BRepOffsetAPI — sweeps, lofts, shells and drafts.
All five derive BRepBuilderAPI_MakeShape, so Build/Shape/history come from
there. MakeThickSolid is the one with an unusual shape: it is default
constructed and then told what to do by MakeThickSolidBySimple (a sheet
thickened into a wall) or MakeThickSolidByJoin (a solid hollowed with
faces removed), each of which does the work — so those get the GIL release
rather than Build().
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <BRepBuilderAPI_MakeShape.hxx>
#include <BRepOffsetAPI_DraftAngle.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
#include <BRepOffsetAPI_MakeThickSolid.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Wire.hxx>
#include <gp_Dir.hxx>
#include <gp_Pln.hxx>
void register_BRepOffsetAPI(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepOffsetAPI");
nb::class_<BRepOffsetAPI_ThruSections, BRepBuilderAPI_MakeShape>(
m, "BRepOffsetAPI_ThruSections")
.def(nb::init<const Standard_Boolean, const Standard_Boolean,
const Standard_Real>(),
"isSolid"_a = Standard_False, "ruled"_a = Standard_False,
"pres3d"_a = 1.0e-06)
.def("AddWire", &BRepOffsetAPI_ThruSections::AddWire, "wire"_a)
.def("CheckCompatibility",
&BRepOffsetAPI_ThruSections::CheckCompatibility, "check"_a = true);
nb::class_<BRepOffsetAPI_MakePipe, BRepBuilderAPI_MakeShape>(
m, "BRepOffsetAPI_MakePipe")
.def(nb::init<const TopoDS_Wire &, const TopoDS_Shape &>(), "Spine"_a,
"Profile"_a, OCP_NOGIL);
nb::class_<BRepOffsetAPI_MakePipeShell, BRepBuilderAPI_MakeShape>(
m, "BRepOffsetAPI_MakePipeShell")
.def(nb::init<const TopoDS_Wire &>(), "Spine"_a)
.def("SetMode",
nb::overload_cast<const Standard_Boolean>(
&BRepOffsetAPI_MakePipeShell::SetMode),
"IsFrenet"_a = Standard_False)
.def("Add",
nb::overload_cast<const TopoDS_Shape &, const Standard_Boolean,
const Standard_Boolean>(
&BRepOffsetAPI_MakePipeShell::Add),
"Profile"_a, "WithContact"_a = Standard_False,
"WithCorrection"_a = Standard_False)
.def("MakeSolid", &BRepOffsetAPI_MakePipeShell::MakeSolid, OCP_NOGIL);
nb::class_<BRepOffsetAPI_MakeThickSolid, BRepBuilderAPI_MakeShape>(
m, "BRepOffsetAPI_MakeThickSolid")
.def(nb::init<>())
.def("MakeThickSolidBySimple",
&BRepOffsetAPI_MakeThickSolid::MakeThickSolidBySimple, "theS"_a,
"theOffsetValue"_a, OCP_NOGIL)
// The trailing mode/join arguments are left off: they are enums the
// app never overrides, and an unregistered enum used as a default
// argument fails at module-init time. OCCT's own defaults apply.
.def(
"MakeThickSolidByJoin",
[](BRepOffsetAPI_MakeThickSolid &self, const TopoDS_Shape &S,
const TopTools_ListOfShape &ClosingFaces,
const Standard_Real Offset, const Standard_Real Tol) {
self.MakeThickSolidByJoin(S, ClosingFaces, Offset, Tol);
},
"S"_a, "ClosingFaces"_a, "Offset"_a, "Tol"_a, OCP_NOGIL);
nb::class_<BRepOffsetAPI_DraftAngle, BRepBuilderAPI_MakeShape>(
m, "BRepOffsetAPI_DraftAngle")
.def(nb::init<const TopoDS_Shape &>(), "S"_a)
.def("Add", &BRepOffsetAPI_DraftAngle::Add, "F"_a, "D"_a, "Angle"_a,
"NeutralPlane"_a, "Flag"_a = Standard_True)
.def("AddDone", &BRepOffsetAPI_DraftAngle::AddDone)
.def("Remove", &BRepOffsetAPI_DraftAngle::Remove, "F"_a);
}

View File

@@ -0,0 +1,70 @@
/*
OCP.BRepTools — assorted topology utilities.
Clean drops an existing triangulation, which the tessellation path does
before re-meshing at a different deflection (OCCT keeps whatever mesh a
shape already carries, so without this the second pass is a no-op).
OuterWire and UVBounds are queries; WireExplorer walks a wire's edges in
connection order rather than map order, which is what wire-following code
needs.
UVBounds reports four scalar out-parameters, so it returns a tuple —
upstream does the same, and `umin, umax, vmin, vmax = ...` is how the app
calls it.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <nanobind/stl/tuple.h>
#include <BRepTools.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Wire.hxx>
void register_BRepTools(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "BRepTools");
nb::class_<BRepTools_WireExplorer>(m, "BRepTools_WireExplorer")
.def(nb::init<>())
.def(nb::init<const TopoDS_Wire &>(), "W"_a)
.def(nb::init<const TopoDS_Wire &, const TopoDS_Face &>(), "W"_a, "F"_a)
.def("Init", nb::overload_cast<const TopoDS_Wire &>(
&BRepTools_WireExplorer::Init),
"W"_a)
.def("More", &BRepTools_WireExplorer::More)
.def("Next", &BRepTools_WireExplorer::Next)
.def("Current", &BRepTools_WireExplorer::Current, OCP_RETURN_COPY)
.def("Orientation", &BRepTools_WireExplorer::Orientation)
.def("CurrentVertex", &BRepTools_WireExplorer::CurrentVertex,
OCP_RETURN_COPY)
.def("Clear", &BRepTools_WireExplorer::Clear);
nb::class_<BRepTools> cls(m, "BRepTools");
OCP_DEF_S(
cls, "Clean",
[](const TopoDS_Shape &S, const Standard_Boolean force) {
BRepTools::Clean(S, force);
},
"S"_a, "force"_a = Standard_False, OCP_NOGIL);
OCP_DEF_S(
cls, "OuterWire",
[](const TopoDS_Face &F) { return BRepTools::OuterWire(F); }, "F"_a);
OCP_DEF_S(
cls, "UVBounds",
[](const TopoDS_Face &F) {
Standard_Real umin = 0.0, umax = 0.0, vmin = 0.0, vmax = 0.0;
BRepTools::UVBounds(F, umin, umax, vmin, vmax);
return std::make_tuple(umin, umax, vmin, vmax);
},
"F"_a);
OCP_DEF_S(
cls, "Update",
[](const TopoDS_Shape &S) { BRepTools::Update(S); }, "S"_a);
}

30
src/modules/mod_GCE2d.cpp Normal file
View File

@@ -0,0 +1,30 @@
/*
OCP.GCE2d — 2D geometry constructors.
Used to lay curves out in a surface's UV space before lifting them onto it
with BRepBuilderAPI_MakeEdge: a thread's helix is a straight line in the
cylinder's parameter space, and curved text emboss draws its glyph
outlines the same way.
*/
#include "../common/occt_module.h"
#include <GCE2d_MakeLine.hxx>
#include <GCE2d_MakeSegment.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt2d.hxx>
void register_GCE2d(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "GCE2d");
nb::class_<GCE2d_MakeLine>(m, "GCE2d_MakeLine")
.def(nb::init<const gp_Pnt2d &, const gp_Dir2d &>(), "P"_a, "V"_a)
.def(nb::init<const gp_Pnt2d &, const gp_Pnt2d &>(), "P1"_a, "P2"_a)
.def("IsDone", &GCE2d_MakeLine::IsDone)
.def("Value", &GCE2d_MakeLine::Value);
nb::class_<GCE2d_MakeSegment>(m, "GCE2d_MakeSegment")
.def(nb::init<const gp_Pnt2d &, const gp_Pnt2d &>(), "P1"_a, "P2"_a)
.def("IsDone", &GCE2d_MakeSegment::IsDone)
.def("Value", &GCE2d_MakeSegment::Value);
}

View File

@@ -0,0 +1,61 @@
/*
OCP.GCPnts — curve discretisation.
TangentialDeflection is what turns every model edge into the polyline the
viewport draws, so it runs once per edge on every tessellation; AbscissaPoint
measures arc length for the edge anchors. Both take an Adaptor3d_Curve,
whose methods are bound on the base in mod_Adaptor3d.cpp.
The constructors compute — there is no deferred form — so they bind as
stock does.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <Adaptor3d_Curve.hxx>
#include <GCPnts_AbscissaPoint.hxx>
#include <GCPnts_TangentialDeflection.hxx>
#include <GCPnts_UniformDeflection.hxx>
#include <gp_Pnt.hxx>
void register_GCPnts(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "GCPnts");
nb::class_<GCPnts_TangentialDeflection>(m, "GCPnts_TangentialDeflection")
.def(nb::init<const Adaptor3d_Curve &, const Standard_Real,
const Standard_Real, const Standard_Integer,
const Standard_Real, const Standard_Real>(),
"C"_a, "AngularDeflection"_a, "CurvatureDeflection"_a,
"MinimumOfPoints"_a = 2, "UTol"_a = 1.0e-9,
"theMinLen"_a = 1.0e-7, OCP_NOGIL)
.def("NbPoints", &GCPnts_TangentialDeflection::NbPoints)
.def("Value", &GCPnts_TangentialDeflection::Value, "I"_a,
OCP_RETURN_COPY)
.def("Parameter", &GCPnts_TangentialDeflection::Parameter, "I"_a);
nb::class_<GCPnts_UniformDeflection>(m, "GCPnts_UniformDeflection")
.def(nb::init<>())
.def(nb::init<const Adaptor3d_Curve &, const Standard_Real,
const Standard_Boolean>(),
"C"_a, "Deflection"_a, "WithControl"_a = Standard_True, OCP_NOGIL)
.def("IsDone", &GCPnts_UniformDeflection::IsDone)
.def("NbPoints", &GCPnts_UniformDeflection::NbPoints)
.def("Value", &GCPnts_UniformDeflection::Value, "Index"_a,
OCP_RETURN_COPY)
.def("Parameter", &GCPnts_UniformDeflection::Parameter, "Index"_a);
nb::class_<GCPnts_AbscissaPoint> abscissa(m, "GCPnts_AbscissaPoint");
abscissa.def(nb::init<>());
OCP_DEF_S(
abscissa, "Length",
[](const Adaptor3d_Curve &C) { return GCPnts_AbscissaPoint::Length(C); },
"C"_a, OCP_NOGIL);
OCP_DEF_S(
abscissa, "Length",
[](const Adaptor3d_Curve &C, const Standard_Real U1,
const Standard_Real U2) {
return GCPnts_AbscissaPoint::Length(C, U1, U2);
},
"C"_a, "U1"_a, "U2"_a, OCP_NOGIL);
}

View File

@@ -0,0 +1,42 @@
/*
OCP.IntCurvesFace — ray casting against a shape.
The surface-pattern feature fires a ray from each sample point along the
inverted normal to find the opposite wall, which is how it measures local
wall thickness. Load prepares the shape once; PerformNearest runs per ray
inside a Python loop, so both release the GIL but neither is a long call.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <IntCurvesFace_ShapeIntersector.hxx>
#include <TopoDS_Shape.hxx>
#include <gp_Lin.hxx>
#include <gp_Pnt.hxx>
void register_IntCurvesFace(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "IntCurvesFace");
nb::class_<IntCurvesFace_ShapeIntersector>(m,
"IntCurvesFace_ShapeIntersector")
.def(nb::init<>())
.def("Load", &IntCurvesFace_ShapeIntersector::Load, "Sh"_a, "Tol"_a,
OCP_NOGIL)
.def("PerformNearest",
nb::overload_cast<const gp_Lin &, const Standard_Real,
const Standard_Real>(
&IntCurvesFace_ShapeIntersector::PerformNearest),
"L"_a, "PInf"_a, "PSup"_a, OCP_NOGIL)
.def("Perform",
nb::overload_cast<const gp_Lin &, const Standard_Real,
const Standard_Real>(
&IntCurvesFace_ShapeIntersector::Perform),
"L"_a, "PInf"_a, "PSup"_a, OCP_NOGIL)
.def("IsDone", &IntCurvesFace_ShapeIntersector::IsDone)
.def("NbPnt", &IntCurvesFace_ShapeIntersector::NbPnt)
.def("Pnt", &IntCurvesFace_ShapeIntersector::Pnt, "I"_a, OCP_RETURN_COPY)
.def("WParameter", &IntCurvesFace_ShapeIntersector::WParameter, "I"_a)
.def("Face", &IntCurvesFace_ShapeIntersector::Face, "I"_a,
OCP_RETURN_COPY);
}

View File

@@ -0,0 +1,61 @@
/*
OCP.ShapeAnalysis — free-boundary detection.
One use: import health counts the edges bounding a single face after a
virtual sew, which is how it tells a watertight solid from a holed shell.
ConnectEdgesToWires is the one place upstream's holder semantics and this
binding's caster differ observably. Its `wires` parameter is a
Handle& that the function *reassigns*, and the caster hands C++ a copy of
the handle rather than a reference into the Python instance — so the
reassignment would be invisible. The lambda splices the result back into
the sequence the caller passed, preserving the pass-empty-then-read shape
the app uses.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include <ShapeAnalysis_FreeBounds.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Shape.hxx>
void register_ShapeAnalysis(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "ShapeAnalysis");
nb::class_<ShapeAnalysis_FreeBounds> cls(m, "ShapeAnalysis_FreeBounds");
cls.def(nb::init<>())
.def(nb::init<const TopoDS_Shape &, const Standard_Real,
const Standard_Boolean, const Standard_Boolean>(),
"shape"_a, "toler"_a, "splitclosed"_a = Standard_False,
"splitopen"_a = Standard_True, OCP_NOGIL)
.def(nb::init<const TopoDS_Shape &, const Standard_Boolean,
const Standard_Boolean, const Standard_Boolean>(),
"shape"_a, "splitclosed"_a = Standard_False,
"splitopen"_a = Standard_True,
"checkinternaledges"_a = Standard_False, OCP_NOGIL)
.def("GetClosedWires", &ShapeAnalysis_FreeBounds::GetClosedWires,
OCP_RETURN_COPY)
.def("GetOpenWires", &ShapeAnalysis_FreeBounds::GetOpenWires,
OCP_RETURN_COPY);
OCP_DEF_S(
cls, "ConnectEdgesToWires",
[](opencascade::handle<TopTools_HSequenceOfShape> &edges,
const Standard_Real toler, const Standard_Boolean shared,
opencascade::handle<TopTools_HSequenceOfShape> &wires) {
opencascade::handle<TopTools_HSequenceOfShape> out;
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges, toler, shared,
out);
// The caller keeps its own sequence object; fill that rather than
// rebinding a handle it cannot see.
if (!wires.IsNull()) {
wires->Clear();
if (!out.IsNull()) {
wires->Append(out->ChangeSequence());
}
}
},
"edges"_a, "toler"_a, "shared"_a, "wires"_a, OCP_NOGIL);
}

View File

@@ -0,0 +1,63 @@
/*
OCP.ShapeFix — geometry healing.
Used on imported geometry and after the operations that are known to
produce slightly invalid output (curved text emboss, thickened sheets).
These are transients, so their constructors go through ocp_new.
Perform() is where the work happens, not Build(), so that is what releases
the GIL.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include "../common/occt_transient.h"
#include <ShapeFix_Face.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_Solid.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
void register_ShapeFix(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "ShapeFix");
nb::class_<ShapeFix_Shape, Standard_Transient>(m, "ShapeFix_Shape")
.def(ocp_new<ShapeFix_Shape>())
.def(ocp_new<ShapeFix_Shape, const TopoDS_Shape &>(), "shape"_a)
.def("Init", nb::overload_cast<const TopoDS_Shape &>(&ShapeFix_Shape::Init),
"shape"_a)
.def(
"Perform", [](ShapeFix_Shape &self) { return self.Perform(); },
OCP_NOGIL)
.def("Shape", &ShapeFix_Shape::Shape, OCP_RETURN_COPY)
.def("SetPrecision", &ShapeFix_Shape::SetPrecision, "preci"_a)
.def("SetMinTolerance", &ShapeFix_Shape::SetMinTolerance, "mintol"_a)
.def("SetMaxTolerance", &ShapeFix_Shape::SetMaxTolerance, "maxtol"_a);
nb::class_<ShapeFix_Face, Standard_Transient>(m, "ShapeFix_Face")
.def(ocp_new<ShapeFix_Face>())
.def(ocp_new<ShapeFix_Face, const TopoDS_Face &>(), "face"_a)
.def(
"Perform", [](ShapeFix_Face &self) { return self.Perform(); },
OCP_NOGIL)
.def("FixOrientation",
nb::overload_cast<>(&ShapeFix_Face::FixOrientation), OCP_NOGIL)
.def("Face", &ShapeFix_Face::Face, OCP_RETURN_COPY);
nb::class_<ShapeFix_Solid, Standard_Transient>(m, "ShapeFix_Solid")
.def(ocp_new<ShapeFix_Solid>())
.def(ocp_new<ShapeFix_Solid, const TopoDS_Solid &>(), "solid"_a)
.def(
"Perform", [](ShapeFix_Solid &self) { return self.Perform(); },
OCP_NOGIL)
.def(
"SolidFromShell",
[](ShapeFix_Solid &self, const TopoDS_Shell &shell) {
return self.SolidFromShell(shell);
},
"shell"_a, OCP_RETURN_COPY, OCP_NOGIL)
.def("Shape", &ShapeFix_Solid::Shape, OCP_RETURN_COPY);
}

View File

@@ -0,0 +1,35 @@
/*
OCP.ShapeUpgrade — merge faces and edges that share a surface.
The app runs this after almost every fuse, so a bidirectional extrude ends
up with the face count of a single prism rather than two prisms glued at
the sketch plane. Not a MakeShape, so Build/Shape are its own.
*/
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include "../common/occt_transient.h"
#include <ShapeUpgrade_UnifySameDomain.hxx>
#include <TopoDS_Shape.hxx>
void register_ShapeUpgrade(nb::module_ &root) {
nb::module_ m = ocp_submodule(root, "ShapeUpgrade");
nb::class_<ShapeUpgrade_UnifySameDomain, Standard_Transient>(
m, "ShapeUpgrade_UnifySameDomain")
.def(ocp_new<ShapeUpgrade_UnifySameDomain>())
.def(ocp_new<ShapeUpgrade_UnifySameDomain, const TopoDS_Shape &,
const Standard_Boolean, const Standard_Boolean,
const Standard_Boolean>(),
"aShape"_a, "UnifyEdges"_a = Standard_True,
"UnifyFaces"_a = Standard_True,
"ConcatBSplines"_a = Standard_False)
.def("Initialize", &ShapeUpgrade_UnifySameDomain::Initialize, "aShape"_a,
"UnifyEdges"_a = Standard_True, "UnifyFaces"_a = Standard_True,
"ConcatBSplines"_a = Standard_False)
.def("SetSafeInputMode", &ShapeUpgrade_UnifySameDomain::SetSafeInputMode,
"theValue"_a)
.def("Build", &ShapeUpgrade_UnifySameDomain::Build, OCP_NOGIL)
.def("Shape", &ShapeUpgrade_UnifySameDomain::Shape, OCP_RETURN_COPY);
}

View File

@@ -10,11 +10,13 @@
#include "../common/occt_module.h"
#include "../common/occt_policies.h"
#include "../common/occt_transient.h"
#include <nanobind/make_iterator.h>
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <TopTools_HSequenceOfShape.hxx>
#include <TopTools_ListOfShape.hxx>
void register_TopTools(nb::module_ &root) {
@@ -97,4 +99,26 @@ void register_TopTools(nb::module_ &root) {
self.Clear(Standard_True);
})
.def("__len__", &TopTools_IndexedDataMapOfShapeListOfShape::Size);
// A transient sequence, unlike the maps above — ShapeAnalysis_FreeBounds
// takes and returns it by handle.
nb::class_<TopTools_HSequenceOfShape, Standard_Transient>(
m, "TopTools_HSequenceOfShape")
.def(ocp_new<TopTools_HSequenceOfShape>())
.def(
"Append",
[](TopTools_HSequenceOfShape &self, const TopoDS_Shape &item) {
self.Append(item);
},
"theItem"_a)
.def("Length", &TopTools_HSequenceOfShape::Length)
.def("IsEmpty", &TopTools_HSequenceOfShape::IsEmpty)
.def(
"Value",
[](const TopTools_HSequenceOfShape &self, Standard_Integer index) {
return self.Value(index);
},
"theIndex"_a, OCP_RETURN_COPY)
.def("Clear", [](TopTools_HSequenceOfShape &self) { self.Clear(); })
.def("__len__", &TopTools_HSequenceOfShape::Length);
}