Geometry Solvers & Builders
Utility and solver types that sit alongside the primary geometry hierarchy: a B-spline curve fitter (BSplineApproxInterp), a thin-plate variational solver (PlateSolver), an N-sided surface-filling builder (FillingSurface), a parametric evolution law (LawFunction), closed-form polynomial root solvers (PolynomialSolver / PolynomialRoots), and a spatial KD-tree (KDTree). Each type is self-contained and constructed independently of Shape or Surface.
Topics
- BSplineApproxInterp · PlateSolver · FillingSurface · LawFunction · PolynomialRoots · PolynomialSolver · KDTree
BSplineApproxInterp
Least-squares B-spline curve approximation through a set of 3D points. Backed by GeomAPI_PointsToBSpline since OCCT 8.0.0p1 (the original Approx_BSplineApproxInterp was removed in that release). Inspect maxError for the worst-case residual after calling perform() or performOptimal().
The API is kept source-compatible with the removed solver, so several of its controls survive as no-ops: interpolatePoint(_:withKink:), setParametrizationAlpha(_:), setMinPivot(_:), setClosedTolerance(_:) and setKnotInsertionTolerance(_:). nbControlPoints and continuousIfClosed are advisory, and performOptimal(maxIterations:) is identical to perform(). The two controls that still bite are setConvergenceTolerance(_:) and setProjectionTolerance(_:), which drive one shared 3D fit tolerance. Each is flagged individually below; the contracts are pinned by BSplineApproxInterpContractTests in Tests/OCCTCurveTests.
init?(points:nbControlPoints:degree:continuousIfClosed:)
Creates a least-squares B-spline approximation solver.
public init?(points: [SIMD3<Double>], nbControlPoints: Int,
degree: Int = 3, continuousIfClosed: Bool = false)
Returns nil if points.count < 2.
- Parameters:
points: array of 3D points to fit.nbControlPoints: advisory number of control points; the approximator chooses the pole count needed to meet tolerance.degree: B-spline degree (default3); widens the fit’s degree range to[min(3, degree), max(degree, 8)].continuousIfClosed: advisory and currently ignored; originally enforced C2 continuity when the curve was detected as closed (defaultfalse).
- Returns: A configured solver, or
nilon invalid input. - OCCT:
GeomAPI_PointsToBSpline(replaces removedApprox_BSplineApproxInterp). - Example:
var pts: [SIMD3<Double>] = [] for i in 0..<50 { let t = Double(i) / 49.0 * 2.0 * .pi pts.append(SIMD3(cos(t), sin(t), 0.1 * t)) } if let solver = BSplineApproxInterp(points: pts, nbControlPoints: 20) { solver.perform() if solver.isDone, let curve = solver.curve { print("Max error:", solver.maxError) } }
interpolatePoint(_:withKink:)
No-op. Originally marked a point to be exactly interpolated (0-based index).
public func interpolatePoint(_ index: Int, withKink: Bool = false)
Note: No-op since OCCT 8.0.0p1.
GeomAPI_PointsToBSplinehas no per-point exact interpolation or C0-break control. The approximation still passes near every point.
- Parameters:
index: ignored; originally a 0-based index into the points array.withKink: ignored; originally inserted a C0 discontinuity at this parameter.
- OCCT: Previously
Approx_BSplineApproxInterp::ChangeConstraints(now inert).
perform()
Perform the fit using automatically computed parameters.
public func perform()
- OCCT:
GeomAPI_PointsToBSplineconstructor (performs the fit on construction). - Example:
solver.perform() if solver.isDone { print(solver.maxError) }
performOptimal(maxIterations:)
Perform the fit. Identical to perform().
public func performOptimal(maxIterations: Int = 10)
Note:
GeomAPI_PointsToBSplinehas no iterative parameter-optimisation mode, so this runs the same single fit asperform()andmaxIterationsis ignored. Kept for source compatibility with the removed solver.
- Parameters:
maxIterations: ignored. - OCCT:
GeomAPI_PointsToBSplineconstructor, same asperform().
isDone
Returns true if the fit was computed successfully.
public var isDone: Bool { get }
- OCCT:
GeomAPI_PointsToBSpline::IsDone.
curve
The resulting B-spline curve, or nil if not done.
public var curve: Curve3D? { get }
- Returns: The fitted
Curve3D, ornilifisDoneisfalse. - OCCT:
GeomAPI_PointsToBSpline::Curve. - Example:
if solver.isDone, let c = solver.curve { print(c.length()) }
maxError
The maximum approximation error.
public var maxError: Double { get }
- Returns: Worst-case 3D distance from any input point to the fitted curve, or
-1if the fit has not run or did not succeed. - OCCT: computed by the bridge.
GeomAPI_PointsToBSplinereports no error, so each input point is projected back onto the fitted curve withGeomAPI_ProjectPointOnCurveand the largestLowerDistance()is returned.
setParametrizationAlpha(_:)
No-op. Originally set the parametrisation power: 0 = uniform, 0.5 = centripetal (default), 1 = chord-length.
public func setParametrizationAlpha(_ alpha: Double)
Note:
GeomAPI_PointsToBSplineselects parametrisation with anApprox_ParametrizationTyperather than an alpha exponent, and the bridge does not currently forward one, so this call has no effect on the fit.
- Parameters:
alpha: ignored.
setMinPivot(_:)
No-op. Originally set the minimum pivot value for the Gauss solver (default 1e-20).
public func setMinPivot(_ value: Double)
Note:
GeomAPI_PointsToBSplineexposes no solver internals, so there is no pivot threshold to set.
setClosedTolerance(_:)
No-op. Originally set the relative tolerance for closed-curve detection (default 1e-12).
public func setClosedTolerance(_ value: Double)
Note:
GeomAPI_PointsToBSplineperforms no closed-curve detection, so there is nothing to tune.
setKnotInsertionTolerance(_:)
No-op. Originally set the tolerance for knot insertion during kink handling (default 1e-4).
public func setKnotInsertionTolerance(_ value: Double)
Note: Kink handling belonged to the removed solver;
GeomAPI_PointsToBSplinehas no equivalent.
setConvergenceTolerance(_:)
Set the 3D fit tolerance (default 1e-3). Values <= 0 are ignored.
public func setConvergenceTolerance(_ value: Double)
This is the primary accuracy control: it becomes Tol3D on the next perform().
- OCCT:
GeomAPI_PointsToBSplineconstructorTol3Dargument.
setProjectionTolerance(_:)
Tighten the 3D fit tolerance to min(current, value) (default 1e-6). Values <= 0 are ignored.
public func setProjectionTolerance(_ value: Double)
Note: This shares one tolerance with
setConvergenceTolerance(_:), so the two are not independent knobs, and this one can only tighten. A value looser than the current tolerance does nothing.
- OCCT:
GeomAPI_PointsToBSplineconstructorTol3Dargument.
PlateSolver
A thin-plate spline solver for smooth surface deformation. Wraps OCCT’s Plate_Plate variational solver: load position and/or derivative pinpoint constraints, call solve(), then evaluate the resulting displacement field at any UV location.
Unlike the higher-level NLPlate methods on Surface, PlateSolver works directly in UV parameter space and returns raw XYZ displacements.
Loading Constraints
init()
Create a new Plate solver.
public init()
- OCCT:
Plate_Platedefault constructor. - Example:
let solver = PlateSolver() solver.loadPinpoint(u: 0, v: 0, position: .zero) solver.loadPinpoint(u: 1, v: 1, position: SIMD3(1, 1, 0.5))
loadPinpoint(u:v:position:)
Load a pinpoint constraint (position at a UV point).
public func loadPinpoint(u: Double, v: Double, position: SIMD3<Double>)
- Parameters:
u,v— UV parameter coordinates.position— target 3D position the surface must pass through.
- OCCT:
Plate_Plate::LoadwithPlate_PinpointConstraint.
loadDerivativeConstraint(u:v:value:derivativeOrderU:derivativeOrderV:)
Load a derivative constraint at a UV point.
public func loadDerivativeConstraint(u: Double, v: Double, value: SIMD3<Double>,
derivativeOrderU: Int, derivativeOrderV: Int)
- Parameters:
u,v— UV parameter coordinates.value— target derivative value.derivativeOrderU— U derivative order (0for position,1+for derivatives).derivativeOrderV— V derivative order.
- OCCT:
Plate_Plate::LoadwithPlate_PinpointConstraintat higher derivative order.
loadGtoC(u:v:sourceD1:targetD1:)
Load a geometric-to-continuity (GtoC) constraint at G1 level.
public func loadGtoC(u: Double, v: Double,
sourceD1: (tangentU: SIMD3<Double>, tangentV: SIMD3<Double>),
targetD1: (tangentU: SIMD3<Double>, tangentV: SIMD3<Double>))
Constrains the surface derivatives to transition from one tangent frame to another at a given UV point.
- Parameters:
u,v— UV parameter coordinates.sourceD1— source surface first derivatives (tangentU, tangentV).targetD1— target surface first derivatives.
- OCCT:
Plate_Plate::LoadwithPlate_GtoCConstraint.
Solving
solve(order:anisotropy:)
Solve the plate system.
@discardableResult
public func solve(order: Int = 4, anisotropy: Double = 1.0) -> Bool
- Parameters:
order— solution polynomial order (default4).anisotropy— anisotropy parameter (default1.0).
- Returns:
trueif the solve succeeded. - OCCT:
Plate_Plate::SolveTI. - Example:
let solver = PlateSolver() solver.loadPinpoint(u: 0.5, v: 0.5, position: SIMD3(0.5, 0.5, 1.0)) if solver.solve() { let pt = solver.evaluate(u: 0.5, v: 0.5) }
isDone
Check if the last solve succeeded.
public var isDone: Bool { get }
- OCCT:
Plate_Plate::IsDone.
Evaluation
evaluate(u:v:)
Evaluate the plate at a UV point.
public func evaluate(u: Double, v: Double) -> SIMD3<Double>
Returns the 3D displacement/position computed by the solver. Must call solve() first.
- Parameters:
u,v— UV parameter coordinates. - Returns: The 3D point on the solved plate surface.
- OCCT:
Plate_Plate::Evaluate.
evaluateDerivative(u:v:derivativeOrderU:derivativeOrderV:)
Evaluate a derivative at a UV point.
public func evaluateDerivative(u: Double, v: Double,
derivativeOrderU: Int, derivativeOrderV: Int) -> SIMD3<Double>
- Parameters:
u,v— UV parameter coordinates.derivativeOrderU— U derivative order.derivativeOrderV— V derivative order.
- Returns: The requested partial derivative vector.
- OCCT:
Plate_Plate::EvaluateDerivative.
uvBox
UV bounding box of the constraint points.
public var uvBox: (umin: Double, umax: Double, vmin: Double, vmax: Double) { get }
- Returns: A tuple describing the UV extent of all loaded constraints.
- OCCT:
Plate_Plate::UVBox.
continuity
Continuity order of the plate solution.
public var continuity: Int { get }
- Returns: Integer continuity order of the computed surface.
- OCCT:
Plate_Plate::Continuity.
FillingSurface
Builder for N-sided surface filling using BRepOffsetAPI_MakeFilling. Creates a smooth surface that satisfies boundary edge constraints and optional interior point constraints. Useful for creating patches that fill holes or connect multiple surface boundaries.
As of #434, FillingSurface shares its bridge implementation with Shape.fill(boundaries:parameters:) and Shape.fill(constraints:parameters:) — same BRepOffsetAPI_MakeFilling builder, same continuity mapping, same untrimmed-pcurve guard (#430). FillingSurface is the incremental, stateful form (build up constraints one call at a time, inspect per-build error introspection); the Shape.fill overloads are one-shot convenience calls over an array of boundaries or constraints.
Refused constraints
An add that returns false did not add its constraint, and that refusal is sticky: build() then returns nil however many other constraints succeeded, rather than fitting a surface to a subset the caller never asked for (#482). This is what Shape.fill(constraints:parameters:) has always done for the same input; build() used to succeed here instead, returning a plausible face that neither passed through nor was bounded by the refused constraint’s edge.
Since every add is @discardableResult, the refusal is recorded whether or not the return value was read. hasRefusedConstraint separates it from an ordinary fitting failure. Both return nil from build().
let filling = FillingSurface()
filling.add(edge: e1, continuity: .g0)
filling.add(edge: rim, support: importedWall, continuity: .g1) // false: no pcurve there
filling.hasRefusedConstraint // true
filling.build() // nil, not a face fitted to e1 alone
To attempt a constraint speculatively and carry on regardless, use add(edge:continuity:), which derives the continuity reference from the edge itself and so has nothing to refuse.
FillingContinuity was one of three copies of the same geometric constraint-order vocabulary, deprecated in #398 as a typealias of SurfaceContinuity (.g0 / .g1 / .g2, no raw value moved), and removed at v2.0.0 (#784). Use SurfaceContinuity directly.
init(degree:pointsOnCurve:maxDegree:maxSegments:tolerance:)
Create a filling surface builder.
public init(degree: Int = 3, pointsOnCurve: Int = 15, maxDegree: Int = 8,
maxSegments: Int = 9, tolerance: Double = 1e-4)
- Parameters:
degree— target polynomial degree (default3).pointsOnCurve— number of discretisation points on each constraint curve (default15).maxDegree— maximum polynomial degree (default8).maxSegments— maximum number of segments (default9).tolerance— 3D tolerance (default1e-4).
- OCCT:
BRepOffsetAPI_MakeFillingconstructor. - Example:
let filling = FillingSurface(degree: 3, tolerance: 1e-5)
add(edge:continuity:)
Add a boundary edge constraint, deriving the continuity reference from the edge’s own underlying surface.
@discardableResult
public func add(edge: Edge, continuity: SurfaceContinuity = .g0) -> Bool
- Parameters:
edge— edge to add as a boundary constraint.continuity— continuity order at this edge (default.g0).
- Returns:
trueif the edge was added successfully. This says nothing about whether the order is usable:Addonly appends, so a bad order surfaces later as anilbuild()that takes every other constraint with it. With no support face to validate, this overload only refuses a constraint OCCT itself throws on; see Refused constraints. - OCCT:
BRepOffsetAPI_MakeFilling::Add(boundary edge variant), via the sameocctFillingContinuityToGeomAbsorder mappingShape.filluses — see Shape-Features for why.g1is notGeomAbs_G1and.g2is notGeomAbs_G2. - Example:
let filling = FillingSurface() for e in someWire.edges() { filling.add(edge: e, continuity: .g0) } if let face = filling.build() { print(face.isValid) }
add(edge:support:continuity:)
Add a boundary edge constraint with an explicit reference face for tangency/curvature.
@discardableResult
public func add(edge: Edge, support: Face, continuity: SurfaceContinuity = .g1) -> Bool
Mirrors FillConstraint’s support-face semantics: a face named here is used or the constraint fails, rather than silently falling back to another surface. Use add(edge:continuity:) to accept whichever surface the edge itself resolves instead.
The failure is not confined to this call. A refusal here poisons build(), which then returns nil whatever else succeeded: the same answer Shape.fill(constraints:parameters:) gives for the same input (#482). See Refused constraints.
support is only meaningful above .g0 — a positional constraint has nothing to be tangent or curvature-continuous with, so at .g0 it is never read. continuity defaults to .g1 rather than .g0 for this reason, matching FillConstraint’s own default.
- Parameters:
edge— edge to add as a boundary constraint.support— face to be continuous with. Used or the constraint fails: if it carries no pcurve foredgeit cannot serve as the continuity reference.continuity— continuity order at this edge (default.g1).
- Returns:
trueif the edge was added successfully.falsemeans the constraint is not in the builder andbuild()will return nil. - OCCT:
BRepOffsetAPI_MakeFilling::Add(edge + support face variant). - Example:
// Tangent to the wall the rim came from let filling = FillingSurface() filling.add(edge: rim, support: wall, continuity: .g1)
add(freeEdge:continuity:)
Add a free (non-boundary) edge constraint.
@discardableResult
public func add(freeEdge edge: Edge, continuity: SurfaceContinuity = .g0) -> Bool
Free edges are not required to be topologically connected to other boundary edges.
- Parameters:
freeEdge— edge to add as a free constraint.continuity— continuity order (default.g0).
- Returns:
trueif the edge was added successfully. - OCCT:
BRepOffsetAPI_MakeFilling::Add(free edge variant).
add(point:)
Add a point constraint that the filling surface must pass through.
@discardableResult
public func add(point: SIMD3<Double>) -> Bool
- Parameters:
point— 3D point the surface must interpolate. - Returns:
trueif the point was added successfully. - OCCT:
BRepOffsetAPI_MakeFilling::Add(point variant).
build()
Build the filling surface and return the resulting shape.
public func build() -> Shape?
Returns nil without attempting the build if any add was refused (#482). Fitting a surface to the constraints that did make it in would answer a different question than the one the caller posed, so the refusal takes the whole build with it, matching Shape.fill(constraints:parameters:).
- Returns: The filled face as a
Shape, ornilif a constraint was refused or the fit failed.hasRefusedConstrainttells the two apart. - OCCT:
BRepOffsetAPI_MakeFilling::Build,BRepOffsetAPI_MakeFilling::Shape. - Example:
let filling = FillingSurface() filling.add(edge: e0, continuity: .g0) filling.add(edge: e1, continuity: .g0) filling.add(edge: e2, continuity: .g0) filling.add(edge: e3, continuity: .g0) if let face = filling.build() { print("G0 error:", filling.g0Error ?? -1) }
isDone
Whether the filling surface has been successfully built.
public var isDone: Bool { get }
- Stays
falseafter a refusedadd, sincebuild()never attempts the fit in that case. - OCCT:
BRepOffsetAPI_MakeFilling::IsDone.
refusedConstraintCount
Number of add calls this builder refused (#482).
public var refusedConstraintCount: Int { get }
A refused constraint is one that is not in the builder: a nominated support face carrying no pcurve for its edge, or a constraint OCCT threw on. Only ever increases: a later successful add does not clear it, because the refused constraint is still missing from the surface that would be fitted.
- OCCT: none. Bridge state, counting constraints never passed to
BRepOffsetAPI_MakeFilling::Add. - Example:
let filling = FillingSurface() filling.add(edge: rim, support: importedWall, continuity: .g1) if filling.refusedConstraintCount > 0 { // build() will return nil; the wall carries no pcurve for the rim }
hasRefusedConstraint
Whether any add was refused, which makes build() return nil (#482).
public var hasRefusedConstraint: Bool { get }
The one signal that separates “a constraint never made it in” from “the fit was attempted and failed”. Both return nil from build().
- OCCT: none. Bridge state:
refusedConstraintCount > 0. - Example:
guard let face = filling.build() else { print(filling.hasRefusedConstraint ? "a constraint was refused" : "the fit failed") return }
g0Error
Positional (G0) error of the built surface.
public var g0Error: Double? { get }
- Returns: Maximum distance from the surface to its constraints, or
nilif not yet built. - OCCT:
BRepOffsetAPI_MakeFilling::G0Error.
g1Error
Tangent (G1) error of the built surface.
public var g1Error: Double? { get }
- Returns: Maximum tangent deviation, or
nilif not yet built. - OCCT:
BRepOffsetAPI_MakeFilling::G1Error.
g2Error
Curvature (G2) error of the built surface.
public var g2Error: Double? { get }
- Returns: Maximum curvature deviation, or
nilif not yet built. - OCCT:
BRepOffsetAPI_MakeFilling::G2Error.
LawFunction
An evolution function defining how a scalar value varies along a parameter range. Used with Shape.pipeShellWithLaw() for variable-section sweeps where the cross-section scales smoothly along the spine path.
Evaluation
value(at:)
Evaluate the law function at a given parameter.
public func value(at parameter: Double) -> Double
- Parameters:
parameter— parameter value withinbounds. - Returns: The scalar value of the law at the given parameter.
- OCCT:
Law_Function::Value. - Example:
if let law = LawFunction.linear(from: 1.0, to: 2.0) { print(law.value(at: 0.5)) // ≈ 1.5 }
bounds
Parameter bounds of the law function.
public var bounds: ClosedRange<Double> { get }
- Returns: The
[first, last]parameter range over which the law is defined. - OCCT:
Law_Function::Bounds.
Factory Methods
constant(_:from:to:)
Create a constant law: the value is uniform over [first, last].
public static func constant(_ value: Double, from first: Double = 0,
to last: Double = 1) -> LawFunction?
- Parameters:
value— the constant scalar output.first,last— parameter range (default0...1).
- Returns: A
LawFunction, ornilon failure. - OCCT:
Law_Constant.
linear(from:to:parameterRange:)
Create a linear law: value ramps from startValue to endValue.
public static func linear(from startValue: Double, to endValue: Double,
parameterRange: ClosedRange<Double> = 0...1) -> LawFunction?
- Parameters:
startValue— value atparameterRange.lowerBound.endValue— value atparameterRange.upperBound.parameterRange— parametric domain (default0...1).
- Returns: A
LawFunction, ornilon failure. - OCCT:
Law_Linear. - Example:
// Scale profile from radius 1 to radius 3 along the sweep if let law = LawFunction.linear(from: 1.0, to: 3.0) { let shape = baseShape.pipeShellWithLaw(spine: spineCurve, law: law) }
sCurve(from:to:parameterRange:)
Create an S-curve law: smooth sigmoid transition between start and end values.
public static func sCurve(from startValue: Double, to endValue: Double,
parameterRange: ClosedRange<Double> = 0...1) -> LawFunction?
- Parameters:
startValue— value at the start of the range.endValue— value at the end of the range.parameterRange— parametric domain (default0...1).
- Returns: A
LawFunction, ornilon failure. - OCCT:
Law_S.
interpolate(points:periodic:)
Create an interpolated law from (parameter, value) pairs.
public static func interpolate(points: [(parameter: Double, value: Double)],
periodic: Bool = false) -> LawFunction?
- Parameters:
points— array of(parameter, value)tuples in ascending parameter order; must have at least 2 elements.periodic— whether the law is periodic (defaultfalse).
- Returns: A
LawFunction, ornilon failure. - OCCT:
Law_Interpol.
bspline(poles:knots:multiplicities:degree:)
Create a BSpline law from control poles and knot vector.
public static func bspline(poles: [Double], knots: [Double],
multiplicities: [Int32],
degree: Int) -> LawFunction?
- Parameters:
poles— control point values (1D); at least 2 required.knots— knot values; at least 2 required.multiplicities— knot multiplicities matchingknots.degree— polynomial degree.
- Returns: A
LawFunction, ornilon failure. - OCCT:
Law_BSpline.
v0.68.0: Composite Law and Knot Splitting
composite(laws:range:)
Create a composite law by stitching multiple sub-laws together.
public static func composite(laws: [LawFunction],
range: ClosedRange<Double> = 0...1) -> LawFunction?
- Parameters:
laws— array of sub-law functions in parameter order; at least 1 required.range— overall parametric range (default0...1).
- Returns: A composite
LawFunction, ornilon failure. - OCCT:
Law_Composite.
knotSplitting(continuityOrder:)
Find knot indices where a BSpline law drops below given continuity.
public func knotSplitting(continuityOrder: ParametricContinuity = .c1) -> [Int]
Only works on BSpline-based law functions created via bspline(poles:knots:multiplicities:degree:). Returns raw indices into the law’s own knot table, not directly usable against value(at:) or bounds; see knotSplitParameters(continuityOrder:) for the parameter-value form.
Every split is returned, however many there are. Before #481 the result was capped at 100: a law with more splits than that reported exactly 100, with nothing to say the rest had been dropped, so it disagreed with knotSplitParameters(continuityOrder:) (same analyzer, same law) about how many splits the law has.
- Parameters:
continuityOrder: minimum continuity to require of each arc. - Returns: Array of knot indices where continuity breaks, or empty array if none or if the function is not BSpline-based.
- OCCT:
Law_BSplineKnotSplitting::NbSplits/SplitValue. - Continuity range (#480): the order is a derivative order, and a knot splits only when
degree - multiplicity < continuityOrder, so the meaningful range is0...degreeand it saturates there. A cubic law with simple interior knots is already C2 there, which means.c0,.c1and.c2all report just the two end knots and.c3is the order that reports the interior ones. - Example:
let indices = law.knotSplitting(continuityOrder: .c2) let params = law.knotSplitParameters(continuityOrder: .c2) // indices[i] is the knot-table index of params[i], so the two always agree on count
knotSplitParameters(continuityOrder:)
Find parameter values (not raw knot indices) where a BSpline law drops below given continuity — the law-function analogue of Curve3D.continuityBreaks.
public func knotSplitParameters(continuityOrder: ParametricContinuity = .c1) -> [Double]
Only works on BSpline-based law functions created via bspline(poles:knots:multiplicities:degree:). Unlike knotSplitting(continuityOrder:)’s raw indices, these are real parameter values, directly usable with value(at:) and bounded by bounds.
- Parameters:
continuityOrder: minimum continuity to require of each arc, same derivative-order contract asknotSplitting(continuityOrder:)above (#480). - Returns: Split parameters in ascending order, or empty array if none or if the function is not BSpline-based.
- OCCT:
Law_BSplineKnotSplitting. - Example:
// A cubic law with simple interior knots is already C2 there, so .c3 is the order that // reports its interior knots; anything below returns just the two end knots. let breaks = law.knotSplitParameters(continuityOrder: .c3) // breaks are real parameters within law.bounds, usable e.g. as sweep split points
PolynomialRoots
Results from polynomial root solving.
roots
The real roots found, sorted ascending.
public let roots: [Double]
count
Number of real roots found.
public var count: Int { get }
- Returns:
roots.count.
PolynomialSolver
Analytical polynomial solvers for degrees 2–4. Uses OCCT’s numerically stable math_DirectPolynomialRoots implementation with Newton-Raphson refinement and degenerate-case handling. All methods are static; there is no instance to create.
quadratic(a:b:c:)
Solve a quadratic equation: ax² + bx + c = 0.
public static func quadratic(a: Double, b: Double, c: Double) -> PolynomialRoots
- Parameters:
a,b,c— coefficients (highest degree first). - Returns:
PolynomialRootscontaining 0, 1, or 2 real roots sorted ascending. - OCCT:
math_DirectPolynomialRoots(degree-2 constructor). - Example:
// x² - 5x + 6 = 0 → x = 2, 3 let r = PolynomialSolver.quadratic(a: 1, b: -5, c: 6) print(r.roots) // [2.0, 3.0]
cubic(a:b:c:d:)
Solve a cubic equation: ax³ + bx² + cx + d = 0.
public static func cubic(a: Double, b: Double, c: Double, d: Double) -> PolynomialRoots
- Parameters:
a,b,c,d— coefficients (highest degree first). - Returns:
PolynomialRootscontaining 1, 2, or 3 real roots sorted ascending. - OCCT:
math_DirectPolynomialRoots(degree-3 constructor). - Example:
// x³ - 6x² + 11x - 6 = 0 → x = 1, 2, 3 let r = PolynomialSolver.cubic(a: 1, b: -6, c: 11, d: -6) print(r.roots) // [1.0, 2.0, 3.0]
quartic(a:b:c:d:e:)
Solve a quartic equation: ax⁴ + bx³ + cx² + dx + e = 0.
public static func quartic(a: Double, b: Double, c: Double, d: Double, e: Double) -> PolynomialRoots
- Parameters:
a,b,c,d,e— coefficients (highest degree first). - Returns:
PolynomialRootscontaining 0–4 real roots sorted ascending. - OCCT:
math_DirectPolynomialRoots(degree-4 constructor). - Example:
// x⁴ - 10x² + 9 = 0 → x = -3, -1, 1, 3 let r = PolynomialSolver.quartic(a: 1, b: 0, c: -10, d: 0, e: 9) print(r.roots) // [-3.0, -1.0, 1.0, 3.0]
KDTree
A KD-tree for fast spatial queries on 3D point sets. Wraps OCCT’s NCollection_KDTree to provide efficient nearest-neighbor, k-nearest, range, and box queries. Build once from an array of points, then query repeatedly.
init?(points:)
Build a KD-tree from an array of 3D points.
public init?(points: [SIMD3<Double>])
- Parameters:
points— the points to index; must be non-empty. - Returns: A KD-tree, or
nilif the input is empty or construction fails. - OCCT:
NCollection_KDTree<gp_Pnt, 3>. - Example:
let pts: [SIMD3<Double>] = [ SIMD3(0, 0, 0), SIMD3(1, 0, 0), SIMD3(0, 1, 0) ] if let tree = KDTree(points: pts) { let nearest = tree.nearest(to: SIMD3(0.1, 0.1, 0)) }
Queries
nearest(to:)
Find the nearest point to a query location.
public func nearest(to point: SIMD3<Double>) -> (index: Int, distance: Double)?
- Parameters:
point— the query point. - Returns: A
(index, distance)tuple whereindexis the 0-based index into the originalpointsarray anddistanceis the Euclidean distance, ornilon error. - OCCT:
NCollection_KDTree::FindNearest. - Example:
if let (idx, dist) = tree.nearest(to: SIMD3(0.4, 0.4, 0)) { print("Nearest index \(idx), distance \(dist)") }
kNearest(to:k:)
Find the K nearest points to a query location.
public func kNearest(to point: SIMD3<Double>, k: Int) -> [(index: Int, squaredDistance: Double)]
- Parameters:
point— the query point.k— output capacity, clamped into0...Sampling.maximumSampleCount(10,000,000); 0 or less returns empty (#622). Fewer thankcome back when the tree holds fewer points.
- Returns: Array of
(index, squaredDistance)tuples sorted by distance. Note: distances are squared. - OCCT:
NCollection_KDTreek-nearest query. - Example:
let neighbors = tree.kNearest(to: SIMD3(0.5, 0.5, 0), k: 3) for (idx, sqDist) in neighbors { print("index \(idx), dist \(sqDist.squareRoot())") }
rangeSearch(center:radius:maxResults:)
Find all points within a sphere.
public func rangeSearch(center: SIMD3<Double>, radius: Double, maxResults: Int = 1000) -> [Int]
- Parameters:
center— center of the search sphere.radius— radius of the search sphere.maxResults— output capacity (default1000), clamped into0...Sampling.maximumSampleCount(10,000,000); 0 or less returns empty (#622).
- Returns: Array of 0-based indices of points within the sphere.
- OCCT:
NCollection_KDTreerange query. - Example:
let nearby = tree.rangeSearch(center: .zero, radius: 1.5) print("\(nearby.count) points within radius 1.5")
boxSearch(min:max:maxResults:)
Find all points within an axis-aligned bounding box.
public func boxSearch(min: SIMD3<Double>, max: SIMD3<Double>, maxResults: Int = 1000) -> [Int]
- Parameters:
min— minimum corner of the box.max— maximum corner of the box.maxResults— output capacity (default1000), clamped into0...Sampling.maximumSampleCount(10,000,000); 0 or less returns empty (#622).
- Returns: Array of 0-based indices of points within the box.
- OCCT:
NCollection_KDTreebox query. - Example:
let inBox = tree.boxSearch(min: SIMD3(0, 0, 0), max: SIMD3(1, 1, 1))