Curve3D — Construction
This page documents higher-level Curve3D construction APIs: arcs of conics (hyperbola, parabola), three-point ellipse and hyperbola factories, periodic conversion, parameter-splitting, expanded interpolation variants, and arc-length utilities. For the core type overview, primitives, BSpline/Bezier construction, operations, and transforms, see the main Curve3D page.
Topics
- Arc Construction, Periodic Conversion, Splitting (v0.50.0) · GC_MakeEllipse/Hyperbola Three-Point Constructors (v0.51.0) · Interpolation Expansion, Length, Closest Point (v0.115.0)
Arc Construction, Periodic Conversion, Splitting (v0.50.0)
Curve3D.arcOfHyperbola(center:direction:majorRadius:minorRadius:alpha1:alpha2:sense:)
Creates a trimmed arc of a hyperbola between two parameter values.
public static func arcOfHyperbola(
center: SIMD3<Double> = .zero,
direction: SIMD3<Double> = SIMD3(0, 0, 1),
majorRadius: Double,
minorRadius: Double,
alpha1: Double,
alpha2: Double,
sense: Bool = true
) -> Curve3D?
The hyperbola lies in the plane whose normal is direction. alpha1 and alpha2 are the start and end hyperbolic parameters (not angles). sense: true preserves the natural parameterization direction.
- Parameters:
center— hyperbola centre;direction— plane normal;majorRadius— real semi-axis (a);minorRadius— imaginary semi-axis (b);alpha1— start parameter;alpha2— end parameter;sense— parameterization direction (true= natural). - Returns: Trimmed hyperbolic arc, or
nilon failure. - OCCT:
GC_MakeArcOfHyperbola(gp_Hypr, alpha1, alpha2, sense)→Geom_TrimmedCurve. - Example:
if let arc = Curve3D.arcOfHyperbola( majorRadius: 5, minorRadius: 3, alpha1: -1.0, alpha2: 1.0 ) { let pts = arc.drawAdaptive() }
Curve3D.arcOfParabola(center:direction:focalDistance:alpha1:alpha2:sense:)
Creates a trimmed arc of a parabola between two parameter values.
public static func arcOfParabola(
center: SIMD3<Double> = .zero,
direction: SIMD3<Double> = SIMD3(0, 0, 1),
focalDistance: Double,
alpha1: Double,
alpha2: Double,
sense: Bool = true
) -> Curve3D?
The parabola vertex is at center and the focal distance determines its opening width. alpha1 and alpha2 are the parabolic parameters bounding the arc.
- Parameters:
center— parabola vertex;direction— plane normal;focalDistance— focal distance (must be > 0);alpha1— start parameter;alpha2— end parameter;sense— parameterization direction. - Returns: Trimmed parabolic arc, or
nilon failure. - OCCT:
GC_MakeArcOfParabola(gp_Parab, alpha1, alpha2, sense)→Geom_TrimmedCurve. - Example:
if let arc = Curve3D.arcOfParabola(focalDistance: 4, alpha1: -3.0, alpha2: 3.0) { let mid = arc.point(at: arc.domain.lowerBound + (arc.domain.upperBound - arc.domain.lowerBound) / 2) }
convertToPeriodic()
Converts a closed BSpline curve to its periodic form.
public func convertToPeriodic() -> Curve3D?
The curve must be closed (start point equals end point). The resulting periodic BSpline seamlessly wraps around without a visible join. Use this before surfaces or wires that require periodic input.
- Returns: Periodic BSpline curve, or
nilif the curve is not closed or periodic conversion is not possible. - OCCT:
ShapeCustom_Curve::ConvertToPeriodic. - Example:
if let closed = Curve3D.interpolate(points: [ SIMD3(1, 0, 0), SIMD3(0, 1, 0), SIMD3(-1, 0, 0), SIMD3(0, -1, 0) ], closed: true) { if let periodic = closed.convertToPeriodic() { #expect(periodic.isPeriodic) } }
SplitResult
A pair of curve segments produced by splitAt(parameter:).
public struct SplitResult {
public let first: Curve3D // segment before the split parameter
public let second: Curve3D // segment after the split parameter
}
splitAt(parameter:)
Splits this curve at a parameter value into two segments.
public func splitAt(parameter: Double) -> SplitResult?
The split parameter must lie strictly inside the curve’s domain (domain.lowerBound < parameter < domain.upperBound). Returns two curves whose domains cover [first, parameter] and [parameter, last] respectively.
- Parameters:
parameter— split position within the open interior ofdomain. - Returns:
SplitResultwithfirstandsecondsegments, ornilifparameteris outside the open interior or splitting fails. - OCCT:
ShapeUpgrade_SplitCurve3d::Perform→TColGeom_HArray1OfCurve. - Example:
if let seg = Curve3D.segment(from: SIMD3(0, 0, 0), to: SIMD3(10, 0, 0)), let result = seg.splitAt(parameter: seg.domain.lowerBound + (seg.domain.upperBound - seg.domain.lowerBound) / 2) { #expect(result.first.endPoint.x < result.second.startPoint.x + 1e-6) }
GC_MakeEllipse/Hyperbola Three-Point Constructors (v0.51.0)
Curve3D.ellipseThreePoints(s1:s2:center:)
Creates a full ellipse defined by two axis endpoints and a centre.
public static func ellipseThreePoints(
s1: SIMD3<Double>, s2: SIMD3<Double>, center: SIMD3<Double>
) -> Curve3D?
s1 is the end of the major axis (determines major radius and axis direction); s2 defines the extent of the minor axis. The plane is determined by the three points. This form is convenient when the three geometric points are known but the radii and normal must be derived.
- Parameters:
s1— major axis endpoint;s2— minor axis extent point;center— ellipse centre. - Returns: Full ellipse curve, or
nilif the three points are degenerate orGC_MakeEllipsefails. - OCCT:
GC_MakeEllipse(gp_Pnt s1, gp_Pnt s2, gp_Pnt center). - Example:
if let ellipse = Curve3D.ellipseThreePoints( s1: SIMD3(10, 0, 0), s2: SIMD3(0, 5, 0), center: .zero ) { let pts = ellipse.drawAdaptive() }
Curve3D.hyperbolaThreePoints(s1:s2:center:)
Creates a full hyperbola defined by two axis endpoints and a centre.
public static func hyperbolaThreePoints(
s1: SIMD3<Double>, s2: SIMD3<Double>, center: SIMD3<Double>
) -> Curve3D?
s1 is the end of the real (transverse) axis; s2 defines the extent of the imaginary (conjugate) axis. Analogous to ellipseThreePoints for hyperbolas.
- Parameters:
s1— transverse axis endpoint;s2— conjugate axis extent point;center— hyperbola centre. - Returns: Full hyperbola curve, or
nilon failure. - OCCT:
GC_MakeHyperbola(gp_Pnt s1, gp_Pnt s2, gp_Pnt center). - Example:
if let hyp = Curve3D.hyperbolaThreePoints( s1: SIMD3(5, 0, 0), s2: SIMD3(0, 3, 0), center: .zero ) { let arc = hyp.trimmed(from: -1.0, to: 1.0) }
Interpolation Expansion, Length, Closest Point (v0.115.0)
Curve3D.interpolate(points:startTangent:endTangent:)
Interpolates a BSpline through points with constrained endpoint tangents.
public static func interpolate(
points: [SIMD3<Double>],
startTangent: SIMD3<Double>,
endTangent: SIMD3<Double>
) -> Curve3D?
Like interpolate(points:closed:tolerance:) but pins the tangent direction at the first and last interpolation points. Useful when the approach and departure directions are known (e.g. G1 join to adjacent geometry).
- Parameters:
points— interpolation points (minimum 2);startTangent— tangent at the first point;endTangent— tangent at the last point. - Returns: Interpolated BSpline, or
nilon failure. - OCCT:
GeomAPI_Interpolate::Load(startTangent, endTangent)+Perform(). - Example:
if let curve = Curve3D.interpolate( points: [SIMD3(0, 0, 0), SIMD3(5, 5, 0), SIMD3(10, 0, 0)], startTangent: SIMD3(1, 0, 0), endTangent: SIMD3(1, 0, 0) ) { let len = curve.totalArcLength }
Curve3D.interpolate(points:tangents:tangentFlags:)
Interpolates a BSpline with per-point tangent constraints.
public static func interpolate(
points: [SIMD3<Double>],
tangents: [SIMD3<Double>],
tangentFlags: [Bool]
) -> Curve3D?
Each point can independently opt in or out of its tangent constraint via tangentFlags. All three arrays must have the same length.
- Parameters:
points— interpolation points;tangents— desired tangent at each point;tangentFlags—trueto enforce the corresponding tangent,falseto ignore it. - Returns: Interpolated BSpline, or
nilif array lengths differ or interpolation fails. - OCCT:
GeomAPI_Interpolate::Load(tangents, tangentFlags)+Perform(). - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(5,3,0), SIMD3(10,0,0)] let tans: [SIMD3<Double>] = [SIMD3(1,1,0), SIMD3(1,0,0), SIMD3(1,-1,0)] let flags = [true, false, true] // enforce only first and last tangents if let curve = Curve3D.interpolate(points: pts, tangents: tans, tangentFlags: flags) { let d = curve.domain #expect(curve.point(at: d.lowerBound).x < 1e-6) }
Curve3D.interpolate(points:parameters:)
Interpolates a BSpline at explicitly specified parameter values.
public static func interpolate(
points: [SIMD3<Double>],
parameters: [Double]
) -> Curve3D?
Controls the parameterization explicitly — each point is placed at the corresponding parameter value. points and parameters must have the same length. Parameters must be strictly increasing.
- Parameters:
points— interpolation points;parameters— parameter value for each point (same count, strictly increasing). - Returns: Interpolated BSpline, or
nilif counts differ or construction fails. - OCCT:
GeomAPI_Interpolate(pts, params, Standard_False, tol)+Perform(). - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(3,5,0), SIMD3(10,0,0)] let params = [0.0, 0.3, 1.0] if let curve = Curve3D.interpolate(points: pts, parameters: params) { #expect(abs(curve.point(at: 0.3).y - 5.0) < 0.5) }
Curve3D.interpolatePeriodic(points:)
Interpolates a periodic (closed) BSpline through the given points.
public static func interpolatePeriodic(points: [SIMD3<Double>]) -> Curve3D?
The resulting curve is periodic: it passes through all points and closes smoothly back to the first point without an explicit closing segment. The first and last points need not coincide.
- Parameters:
points— interpolation points (minimum 2, typically 3 or more for a non-degenerate loop). - Returns: Periodic interpolated BSpline, or
nilon failure. - OCCT:
GeomAPI_Interpolate(pts, Standard_True, tol)+Perform(). - Example:
if let loop = Curve3D.interpolatePeriodic(points: [ SIMD3(1, 0, 0), SIMD3(0, 1, 0), SIMD3(-1, 0, 0), SIMD3(0, -1, 0) ]) { #expect(loop.isPeriodic) }
Curve3D.approximate(points:degMin:degMax:continuity:tolerance:)
Approximates a BSpline through points with degree and continuity control.
public static func approximate(
points: [SIMD3<Double>],
degMin: Int = 3,
degMax: Int = 8,
continuity: Int = 2,
tolerance: Double = 1e-3
) -> Curve3D?
Unlike interpolate, the curve does not pass exactly through every point; it minimises deviation within tolerance. continuity maps to GeomAbs_Shape: 0=C0, 1=G1, 2=C1.
- Parameters:
points— data points (minimum 2);degMin— minimum polynomial degree;degMax— maximum polynomial degree;continuity— minimum continuity order (0=C0, 1=G1, 2=C1);tolerance— maximum allowed deviation. - Returns: Approximating BSpline, or
nilon failure. - OCCT:
GeomAPI_PointsToBSpline(pts, degMin, degMax, continuity, tolerance). - Example:
let pts: [SIMD3<Double>] = [ SIMD3(0,0,0), SIMD3(2,3,0), SIMD3(5,1,0), SIMD3(8,4,0), SIMD3(10,0,0) ] if let curve = Curve3D.approximate(points: pts, degMin: 3, degMax: 8, continuity: 2, tolerance: 1e-3) { let len = curve.totalArcLength }
Curve3D.approximate(points:parameters:degMin:degMax:continuity:tolerance:)
Approximates a BSpline with explicit parameter values at each point.
public static func approximate(
points: [SIMD3<Double>],
parameters: [Double],
degMin: Int = 3,
degMax: Int = 8,
continuity: Int = 2,
tolerance: Double = 1e-3
) -> Curve3D?
Combines approximation with explicit parameterization — useful when the data comes with known parameter stamps (e.g. timestamps or arc-length fractions).
- Parameters:
points— data points;parameters— parameter for each point (same count, strictly increasing);degMin/degMax— degree bounds;continuity— minimum continuity;tolerance— maximum deviation. - Returns: Approximating BSpline, or
nilif counts differ or construction fails. - OCCT:
GeomAPI_PointsToBSpline(pts, params, degMin, degMax, continuity, tolerance). - Example:
let pts: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(3,5,0), SIMD3(10,0,0)] let params = [0.0, 0.3, 1.0] if let curve = Curve3D.approximate(points: pts, parameters: params, degMin: 2, degMax: 6) { let len = curve.totalArcLength }
arcLength(from:to:)
The arc length of this curve between two parameter values.
public func arcLength(from u1: Double, to u2: Double) -> Double
Non-optional; returns 0 if the curve is invalid or an exception occurs internally.
- Parameters:
u1— start parameter;u2— end parameter (must satisfyu1 ≤ u2). - Returns: Arc length in model units.
- OCCT:
GCPnts_AbscissaPoint::Length(GeomAdaptor_Curve(curve, u1, u2)). - Example:
if let curve = Curve3D.interpolate( points: [SIMD3(0,0,0), SIMD3(10,0,0)], startTangent: SIMD3(1,0,0), endTangent: SIMD3(1,0,0) ) { let d = curve.domain let len = curve.arcLength(from: d.lowerBound, to: d.upperBound) #expect(len > 0) }
parameterAtLength(_:from:)
Finds the parameter at a given arc-length distance from a starting parameter.
public func parameterAtLength(_ arcLength: Double, from startParam: Double? = nil) -> Double
Uses GCPnts_AbscissaPoint for accurate arc-length parameterisation. Positive arcLength advances forward; negative reverses. Returns 0 on internal failure.
- Parameters:
arcLength— distance to advance along the curve;startParam— starting parameter (defaults todomain.lowerBound). - Returns: The parameter value at the specified arc-length distance from
startParam. - OCCT:
GCPnts_AbscissaPoint(adaptor, arcLength, startParam)::Parameter. - Example:
let line = Curve3D.segment(from: SIMD3(0,0,0), to: SIMD3(10,0,0))! let midParam = line.parameterAtLength(5.0) let midPt = line.point(at: midParam) #expect(abs(midPt.x - 5.0) < 0.01)
totalArcLength
The total arc length of the curve over its full domain.
public var totalArcLength: Double { get }
Integrates the curve from domain.lowerBound to domain.upperBound. Distinct from the length property on the main page (which returns Double?); this always returns a Double (0 on failure).
- Returns: Total arc length in model units.
- OCCT:
GCPnts_AbscissaPoint::Length(GeomAdaptor_Curve(curve)). - Example:
let circle = Curve3D.circle(center: .zero, normal: SIMD3(0,0,1), radius: 10)! let circumference = circle.totalArcLength #expect(abs(circumference - 2 * Double.pi * 10) < 0.1)
arcLengthBetween(_:_:)
The arc length between two parameter values.
public func arcLengthBetween(_ param1: Double, _ param2: Double) -> Double
Like arcLength(from:to:) but with unlabelled parameters. Returns 0 on failure.
- Parameters:
param1— first parameter;param2— second parameter. - Returns: Arc length between the two parameters.
- OCCT:
GCPnts_AbscissaPoint::Length(adaptor, param1, param2). - Example:
let line = Curve3D.segment(from: SIMD3(0,0,0), to: SIMD3(10,0,0))! let d = line.domain let half = line.arcLengthBetween(d.lowerBound, (d.lowerBound + d.upperBound) / 2) #expect(abs(half - 5.0) < 0.01)
closestParameter(to:)
Finds the parameter of the closest point on this curve to a given 3D point.
public func closestParameter(to point: SIMD3<Double>) -> Double
Uses a projection; returns 0 if projection finds no points or an exception occurs.
- Parameters:
point— the query point in 3D space. - Returns: Parameter
usuch thatself.point(at: u)is the nearest point on the curve topoint. - OCCT:
GeomAPI_ProjectPointOnCurve::LowerDistanceParameter. - Example:
if let line = Curve3D.line(through: .zero, direction: SIMD3(1,0,0)) { let param = line.closestParameter(to: SIMD3(5, 3, 0)) #expect(abs(param - 5.0) < 0.1) }
splitAtContinuity(continuity:tolerance:maxSegments:)
Splits this curve at continuity discontinuities, returning the resulting segments.
public func splitAtContinuity(
continuity: Int = 1,
tolerance: Double = 1e-6,
maxSegments: Int = 32
) -> [Curve3D]
The curve is first converted to BSpline form, then split at C1 (or higher) discontinuities. For continuity > 1 the implementation returns the single converted BSpline unchanged (higher-order splitting not yet implemented). Returns an empty array on failure.
- Parameters:
continuity— continuity level to split at (0=C0, 1=C1);tolerance— discontinuity detection tolerance;maxSegments— maximum output segment count. - Returns: Array of BSpline curve segments (at least one on success).
- OCCT:
GeomConvert::C0BSplineToArrayOfC1BSplineCurve(forcontinuity ≤ 1). - Example:
if let curve = Curve3D.fit(points: [SIMD3(0,0,0), SIMD3(5,5,0), SIMD3(10,0,0)]) { let segs = curve.splitAtContinuity() #expect(segs.count >= 1) }
Curve3D.concatenateG1(curves:tolerance:)
Concatenates an array of curves into a single BSpline with G1 continuity.
public static func concatenateG1(curves: [Curve3D], tolerance: Double = 1e-6) -> Curve3D?
Each input curve is converted to BSpline form before joining. Curves must connect end-to-end within tolerance.
- Parameters:
curves— ordered array of curves to join;tolerance— endpoint gap tolerance. - Returns: Joined BSpline curve, or
nilif the array is empty or conversion fails. - OCCT:
GeomConvert_CompCurveToBSplineCurve::Add+BSplineCurve(). - Example:
let pts1: [SIMD3<Double>] = [SIMD3(0,0,0), SIMD3(5,5,0), SIMD3(10,0,0)] let pts2: [SIMD3<Double>] = [SIMD3(10,0,0), SIMD3(15,-5,0), SIMD3(20,0,0)] if let c1 = Curve3D.fit(points: pts1), let c2 = Curve3D.fit(points: pts2), let joined = Curve3D.concatenateG1(curves: [c1, c2]) { #expect(joined.totalArcLength > c1.totalArcLength) }