Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 40 additions & 30 deletions Sources/OCCTBridge/include/OCCTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,26 @@
// --- GeomConvert ---
// GeomConvert → OCCTCurve3DToBSpline, OCCTCurve3DBSplineToBeziers, OCCTCurve3DSplitAtContinuity,
// OCCTSurfaceToBSpline, OCCTSurfaceToBezierPatches
// GeomConvert_BSplineCurveKnotSplitting → OCCTCurve3DBSplineKnotSplits (the sole wrapper; #562)
// GeomConvert_BSplineSurfaceKnotSplitting → OCCTSurfaceKnotSplitting (the sole wrapper since #562
// deleted the second family that wrapped it)
// GeomConvert_CompCurveToBSplineCurve → OCCTCurve3DJoinCurves, OCCTCurve3DJoinToBSpline,
// OCCTCurve3DConcatenateG1, OCCTConcatenateCurves3D
// GeomConvert_CurveToAnaCurve → OCCTGeomConvertCurveToAnalytical, OCCTGeomConvertIsLinear
// GeomConvert_SurfToAnaSurf → OCCTGeomConvertSurfToAnalytical*, OCCTGeomConvertIsCanonical
//
// --- Geom2dConvert ---
// #562: this section did not exist, which is half of why Geom2dConvert_BSplineCurveKnotSplitting
// could be wrapped twice without anything noticing. Census is by call site in OCCTBridge_Geom2d.mm.
// Geom2dConvert → OCCTCurve2DToBSpline, OCCTCurve2DSplitAtContinuity,
// OCCTCurve2DJoinToBSpline
// Geom2dConvert_ApproxArcsSegments → OCCTGeom2dConvertApproxArcsSegments, OCCTCurve2DToArcsAndSegments
// Geom2dConvert_ApproxCurve → OCCTCurve2DApproximate
// Geom2dConvert_BSplineCurveKnotSplitting → OCCTCurve2DSplitAtDiscontinuities (the sole wrapper
// since #562 deleted the second family that wrapped it)
// Geom2dConvert_BSplineCurveToBezierCurve → OCCTCurve2DBSplineToBeziers
// Geom2dConvert_CompCurveToBSplineCurve → OCCTConcatenateCurves2D, OCCTCurve2DJoinToBSpline
//
// --- Convert ---
// Convert_CompBezierCurvesToBSplineCurve → OCCTConvertCompBezierToBSpline (v0.99.0)
// Convert_CompBezierCurves2dToBSplineCurve2d → OCCTConvertCompBezier2dToBSpline2d (v0.99.0)
Expand Down Expand Up @@ -2663,6 +2678,10 @@ OCCTCurve2DRef OCCTCurve2DApproximate(OCCTCurve2DRef curve, double tolerance,
int32_t continuity, int32_t maxSegments, int32_t maxDegree);
// `continuity` is a ContinuityRange (a literal derivative order, splitting where
// `degree - multiplicity < continuity`), not a GeomAbs_Shape. See the #480 note in OCCTBridge_Internal.h.
// Returns the TRUE split count even when writing was truncated by `max`, so a caller that came up
// short can retry at the size it was just told — the #481 contract the rest of this family already
// shares. It used to return the count it had written, which is indistinguishable from a curve with
// exactly `max` splits (#562).
int32_t OCCTCurve2DSplitAtDiscontinuities(OCCTCurve2DRef curve, int32_t continuity,
int32_t* outKnotIndices, int32_t max);
int32_t OCCTCurve2DToArcsAndSegments(OCCTCurve2DRef curve, double tolerance,
Expand Down Expand Up @@ -6832,15 +6851,20 @@ typedef struct {
/// cubic with simple interior knots needs 3. See the #480 note in OCCTBridge_Internal.h
/// @param vContinuity Desired V continuity, same contract against the V degree and knots
/// @param outUParams Pre-allocated array for U split parameter values (may be NULL)
/// @param maxUParams Capacity of outUParams
/// @param outUIndices Pre-allocated array for the 1-based U knot-table indices those parameters
/// were read from, i.e. `outUParams[i] == UKnot(outUIndices[i])` (may be NULL). #562: the
/// analyzer reports indices and this function converts them, so the caller only ever saw the
/// converted form and a second family of bridge functions existed to serve the raw one
/// @param maxU Capacity of outUParams and outUIndices
/// @param outVParams Pre-allocated array for V split parameter values (may be NULL)
/// @param maxVParams Capacity of outVParams
/// @param outVIndices Pre-allocated array for the 1-based V knot-table indices (may be NULL)
/// @param maxV Capacity of outVParams and outVIndices
/// @return Split counts; nbUSplits/nbVSplits are the true counts even when writing
/// was truncated by maxUParams/maxVParams, so a caller can retry with a bigger buffer
/// was truncated by maxU/maxV, so a caller can retry with a bigger buffer
OCCTSurfaceKnotSplitResult OCCTSurfaceKnotSplitting(OCCTSurfaceRef surface,
int32_t uContinuity, int32_t vContinuity,
double* outUParams, int32_t maxUParams,
double* outVParams, int32_t maxVParams);
double* outUParams, int32_t* outUIndices, int32_t maxU,
double* outVParams, int32_t* outVIndices, int32_t maxV);

/// Join an array of Bezier surface patches into a single BSpline surface.
/// @param patches Array of surface handles (row-major, nRows x nCols)
Expand Down Expand Up @@ -14678,33 +14702,19 @@ OCCTCurve3DRef _Nullable OCCTConcatenateCurves3D(OCCTCurve3DRef _Nonnull * _Nonn
OCCTCurve2DRef _Nullable OCCTConcatenateCurves2D(OCCTCurve2DRef _Nonnull * _Nonnull curves,
int32_t count, double tolerance);

// MARK: - GeomConvert_BSplineSurfaceKnotSplitting (v0.105.0)
// MARK: - GeomConvert_BSplineSurfaceKnotSplitting / Geom2dConvert_BSplineCurveKnotSplitting
//
// `continuity` throughout this section is the same ContinuityRange as OCCTSurfaceKnotSplitting
// takes: a literal derivative order, splitting where `degree - multiplicity < continuity`, with
// useful domain 0...degree. See the #480 note in OCCTBridge_Internal.h.

/// Get number of U-direction knot splits for a BSpline surface at given continuity.
int32_t OCCTBSplineSurfaceKnotSplitsU(OCCTSurfaceRef _Nonnull surface, int32_t continuity);

/// Get number of V-direction knot splits for a BSpline surface at given continuity.
int32_t OCCTBSplineSurfaceKnotSplitsV(OCCTSurfaceRef _Nonnull surface, int32_t continuity);

/// Get U and V knot split indices for a BSpline surface at given continuity.
void OCCTBSplineSurfaceKnotSplitValues(OCCTSurfaceRef _Nonnull surface, int32_t continuity,
int32_t* _Nonnull uSplits, int32_t* _Nonnull vSplits);

// MARK: - Geom2dConvert_BSplineCurveKnotSplitting (v0.105.0)
// #562: five functions used to live here (OCCTBSplineSurfaceKnotSplitsU/V,
// OCCTBSplineSurfaceKnotSplitValues, OCCTBSplineCurve2dKnotSplits,
// OCCTBSplineCurve2dKnotSplitValues), added in v0.105.0 over the same two analyzers
// OCCTSurfaceKnotSplitting and OCCTCurve2DSplitAtDiscontinuities already drove. They are gone;
// those two are the sole wrappers of their analyzer. Both now report the split knot-table
// indices, which is all the deleted family carried that the survivors did not.
//
// Same ContinuityRange contract again: Geom2dConvert_BSplineCurveKnotSplitting runs the
// identical algorithm on a 2D curve. See the #480 note in OCCTBridge_Internal.h.

/// Get number of knot splits for a 2D BSpline curve at given continuity.
int32_t OCCTBSplineCurve2dKnotSplits(OCCTCurve2DRef _Nonnull curve, int32_t continuity);

/// Get knot split indices for a 2D BSpline curve at given continuity.
void OCCTBSplineCurve2dKnotSplitValues(OCCTCurve2DRef _Nonnull curve, int32_t continuity,
int32_t* _Nonnull splits);
// Two contract hazards the deleted family had, recorded so they are not reintroduced: neither
// values function took a buffer capacity (each wrote NbSplits() entries into a buffer the caller
// had sized from a *separate* call), and the surface one constructed the analyzer three times per
// logical query, once per count call and once for the values.

// MARK: - BndLib extras (v0.105.0)

Expand Down
45 changes: 12 additions & 33 deletions Sources/OCCTBridge/src/OCCTBridge_Geom2d.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3375,32 +3375,11 @@ OCCTCurve2DRef OCCTConcatenateCurves2D(OCCTCurve2DRef* curves, int32_t count, do
return r;
} catch (...) { return nullptr; }
}
// MARK: - Geom2dConvert_BSplineCurveKnotSplitting (v0.105.0)

#include <Geom2dConvert_BSplineCurveKnotSplitting.hxx>

int32_t OCCTBSplineCurve2dKnotSplits(OCCTCurve2DRef curve, int32_t continuity) {
if (!curve) return 0;
try {
Handle(Geom2d_BSplineCurve) bc = Handle(Geom2d_BSplineCurve)::DownCast(curve->curve);
if (bc.IsNull()) return 0;
Geom2dConvert_BSplineCurveKnotSplitting splitter(bc, continuity);
return (int32_t)splitter.NbSplits();
} catch (...) { return 0; }
}

void OCCTBSplineCurve2dKnotSplitValues(OCCTCurve2DRef curve, int32_t continuity,
int32_t* splits) {
if (!curve || !splits) return;
try {
Handle(Geom2d_BSplineCurve) bc = Handle(Geom2d_BSplineCurve)::DownCast(curve->curve);
if (bc.IsNull()) return;
Geom2dConvert_BSplineCurveKnotSplitting splitter(bc, continuity);
for (int i = 1; i <= splitter.NbSplits(); i++) {
splits[i - 1] = splitter.SplitValue(i);
}
} catch (...) {}
}
// #562: OCCTBSplineCurve2dKnotSplits and OCCTBSplineCurve2dKnotSplitValues stood here, a second
// wrap of Geom2dConvert_BSplineCurveKnotSplitting added three releases after
// OCCTCurve2DSplitAtDiscontinuities (further down this file) already wrapped it. Deleted; that
// one returns the same indices, and now reports the true count when truncated, which is the one
// respect in which these were the stronger pair rather than the weaker.

// MARK: - v0.106: BRepLib_MakeEdge2d extensions + Curve2D continuity
// MARK: - BRepLib_MakeEdge2d extensions (v0.106.0)
Expand Down Expand Up @@ -5966,20 +5945,20 @@ Geom2dConvert_ApproxCurve approx(c->curve, tolerance,
}
}

// #562: reports the TRUE split count even when `max` truncated the write, so the Swift caller can
// retry at the size it was just told -- the #481 contract shared by every other member of this
// family. It used to return the written count, which capped it silently at its caller's 256-entry
// first pass and was indistinguishable from a curve with exactly 256 splits.
int32_t OCCTCurve2DSplitAtDiscontinuities(OCCTCurve2DRef c, int32_t continuity,
int32_t* outKnotIndices, int32_t max) {
if (!c || c->curve.IsNull() || !outKnotIndices || max <= 0) return 0;
try {
Handle(Geom2d_BSplineCurve) bsp = Handle(Geom2d_BSplineCurve)::DownCast(c->curve);
if (bsp.IsNull()) return 0;
Geom2dConvert_BSplineCurveKnotSplitting splitter(bsp, continuity);
int32_t n = std::min((int32_t)splitter.NbSplits(), max);
TColStd_Array1OfInteger indices(1, splitter.NbSplits());
splitter.Splitting(indices);
for (int32_t i = 0; i < n; i++) {
outKnotIndices[i] = indices(i + 1);
}
return n;
return occtWriteKnotSplits<int32_t>(splitter.NbSplits(),
[&](int32_t i) { return (int32_t)splitter.SplitValue(i); },
outKnotIndices, max);
} catch (...) {
return 0;
}
Expand Down
73 changes: 24 additions & 49 deletions Sources/OCCTBridge/src/OCCTBridge_Surface.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1812,12 +1812,14 @@ OCCTSurfaceRef OCCTSurfaceTrimmedCylinder(
// MARK: - Surface KnotSplitting / JoinBezierPatches (v0.50)
// #403: also fills the U/V split PARAMETER buffers (not just the counts) -- the
// underlying GeomConvert_BSplineSurfaceKnotSplitting analyzer always computed
// USplitValue/VSplitValue, this just wasn't surfaced. outUParams/outVParams may be
// null (or their max 0) to skip writing either direction.
// USplitValue/VSplitValue, this just wasn't surfaced. #562: and fills the raw knot-table
// indices those parameters came from, which is what a second, now-deleted family of bridge
// functions existed to return. Any out buffer may be null (or its max 0) to skip it; one
// analyzer construction serves all four, where that family needed three.
OCCTSurfaceKnotSplitResult OCCTSurfaceKnotSplitting(OCCTSurfaceRef surface,
int32_t uContinuity, int32_t vContinuity,
double* outUParams, int32_t maxUParams,
double* outVParams, int32_t maxVParams) {
double* outUParams, int32_t* outUIndices, int32_t maxU,
double* outVParams, int32_t* outVIndices, int32_t maxV) {
OCCTSurfaceKnotSplitResult result = {};
if (!surface) return result;
try {
Expand All @@ -1826,17 +1828,27 @@ OCCTSurfaceKnotSplitResult OCCTSurfaceKnotSplitting(OCCTSurfaceRef surface,
GeomConvert_BSplineSurfaceKnotSplitting splitter(bsurf, uContinuity, vContinuity);
result.nbUSplits = splitter.NbUSplits();
result.nbVSplits = splitter.NbVSplits();
if (outUParams && maxUParams > 0) {
if (outUParams && maxU > 0) {
occtWriteKnotSplitParams(result.nbUSplits,
[&](int32_t i) { return splitter.USplitValue(i); },
[&](int32_t idx) { return bsurf->UKnot(idx); },
outUParams, maxUParams);
outUParams, maxU);
}
if (outVParams && maxVParams > 0) {
if (outUIndices && maxU > 0) {
occtWriteKnotSplits<int32_t>(result.nbUSplits,
[&](int32_t i) { return (int32_t)splitter.USplitValue(i); },
outUIndices, maxU);
}
if (outVParams && maxV > 0) {
occtWriteKnotSplitParams(result.nbVSplits,
[&](int32_t i) { return splitter.VSplitValue(i); },
[&](int32_t idx) { return bsurf->VKnot(idx); },
outVParams, maxVParams);
outVParams, maxV);
}
if (outVIndices && maxV > 0) {
occtWriteKnotSplits<int32_t>(result.nbVSplits,
[&](int32_t i) { return (int32_t)splitter.VSplitValue(i); },
outVIndices, maxV);
}
} catch (...) {}
return result;
Expand Down Expand Up @@ -3805,47 +3817,10 @@ bool OCCTBRepGPropFaceBoundaryIntegration(OCCTShapeRef face, int32_t edgeIndex,
} catch (...) { return false; }
}

// MARK: - v0.105: GeomConvert_BSplineSurfaceKnotSplitting
// MARK: - GeomConvert_BSplineSurfaceKnotSplitting (v0.105.0)

#include <GeomConvert_BSplineSurfaceKnotSplitting.hxx>
#include <Geom_BSplineSurface.hxx>

int32_t OCCTBSplineSurfaceKnotSplitsU(OCCTSurfaceRef surface, int32_t continuity) {
if (!surface) return 0;
try {
Handle(Geom_BSplineSurface) bsurf = Handle(Geom_BSplineSurface)::DownCast(surface->surface);
if (bsurf.IsNull()) return 0;
GeomConvert_BSplineSurfaceKnotSplitting splitter(bsurf, continuity, continuity);
return (int32_t)splitter.NbUSplits();
} catch (...) { return 0; }
}

int32_t OCCTBSplineSurfaceKnotSplitsV(OCCTSurfaceRef surface, int32_t continuity) {
if (!surface) return 0;
try {
Handle(Geom_BSplineSurface) bsurf = Handle(Geom_BSplineSurface)::DownCast(surface->surface);
if (bsurf.IsNull()) return 0;
GeomConvert_BSplineSurfaceKnotSplitting splitter(bsurf, continuity, continuity);
return (int32_t)splitter.NbVSplits();
} catch (...) { return 0; }
}

void OCCTBSplineSurfaceKnotSplitValues(OCCTSurfaceRef surface, int32_t continuity,
int32_t* uSplits, int32_t* vSplits) {
if (!surface || !uSplits || !vSplits) return;
try {
Handle(Geom_BSplineSurface) bsurf = Handle(Geom_BSplineSurface)::DownCast(surface->surface);
if (bsurf.IsNull()) return;
GeomConvert_BSplineSurfaceKnotSplitting splitter(bsurf, continuity, continuity);
for (int i = 1; i <= splitter.NbUSplits(); i++) {
uSplits[i - 1] = splitter.USplitValue(i);
}
for (int i = 1; i <= splitter.NbVSplits(); i++) {
vSplits[i - 1] = splitter.VSplitValue(i);
}
} catch (...) {}
}
// #562: OCCTBSplineSurfaceKnotSplitsU/V and OCCTBSplineSurfaceKnotSplitValues stood here,
// a second wrap of GeomConvert_BSplineSurfaceKnotSplitting added three releases after
// OCCTSurfaceKnotSplitting (line 1817 of this file) already wrapped it. Deleted; that one now
// reports the split knot-table indices too, which is the only thing these carried that it did not.

// MARK: - v0.106: GC_MakeConical/Cylindrical/TrimmedCone/TrimmedCylinder + Surface continuity
// MARK: - GC_MakeConicalSurface (v0.106.0)
Expand Down
17 changes: 14 additions & 3 deletions Sources/OCCTSwift/Curve2D.swift
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,21 @@ public final class Curve2D: @unchecked Sendable {
/// saturates there (#480).
/// - Returns: Array of knot indices where the curve drops below the requested continuity, or nil if not a B-spline.
public func splitIndicesAtDiscontinuities(continuity: ParametricContinuity = .c1) -> [Int]? {
var buffer = [Int32](repeating: 0, count: 256)
let n = Int(OCCTCurve2DSplitAtDiscontinuities(handle, continuity.rawValue, &buffer, 256))
// Read-then-retry, the #481 pattern the rest of this family shares: the bridge reports the
// true split count even when it wrote fewer, so one retry sized to it is always enough.
// Before #562 this read a fixed 256 entries and took whatever came back, so a curve with
// more splits than that was silently cut off at 256 with nothing to notice it by.
func read(capacity: Int) -> (count: Int, buffer: [Int32]) {
var buffer = [Int32](repeating: 0, count: capacity)
let n = Int(OCCTCurve2DSplitAtDiscontinuities(handle, continuity.rawValue,
&buffer, Int32(capacity)))
return (n, buffer)
}

var (n, buffer) = read(capacity: 256)
guard n > 0 else { return nil }
return (0..<n).map { Int(buffer[$0]) }
if n > 256 { (n, buffer) = read(capacity: n) }
return buffer.prefix(n).map(Int.init)
}

/// Approximate this curve as a sequence of arcs and line segments.
Expand Down
Loading
Loading