Link Search Menu Expand Document

Curve2D — Analysis

This page covers the analysis and query members of Curve2D: differential geometry (curvature, normal, inflection), bounding box, general-purpose intersection/projection/extrema, batch evaluation, analytical intersection primitives, 2D extrema solvers, detailed curvature-inflection classification via Geom2dLProp, approximation/simplification via ShapeCustom_Curve2d and Approx_Curve2d, interpolation, arc-length and trim, local extrema and gce factory construction, serialization/persistence, energy-minimal fair curves, and Point2D integration. For primitive construction, B-spline/Bezier operations, and transforms see the main Curve2D page.

Topics


Local Properties (Curvature, Normal, Inflection)

Differential geometry queries evaluated at a parametric point on the curve, backed by Geom2dLProp_CLProps2d.


curvature(at:)

Returns the signed curvature (1 / radius of curvature) at parameter u.

public func curvature(at u: Double) -> Double?

Returns 0 for straight segments — a real answer — and nil where GeomLProp_CLProps2d::IsTangentDefined() is false. Those two used to be the same 0 (#595).

  • Parameters: u — curve parameter.
  • Returns: Curvature value, 0 for a straight segment, or nil where the curve has no tangent there (a Bezier whose control points all coincide) or the parameter cannot be evaluated. A cusp still reports Double.greatestFiniteMagnitude (OCCT’s RealLast(), meaning infinite curvature): an answer, not an absence.
  • OCCT: Geom2dLProp_CLProps2d::Curvature.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5) {
        let k = circle.curvature(at: 0)  // ≈ 0.2 (1/R)
    }
    if let seg = Curve2D.segment(from: SIMD2(0, 0), to: SIMD2(10, 0)) {
        seg.curvature(at: 5)             // 0 — straight, and that is the answer
    }
    

normal(at:)

Returns the unit inward normal vector at parameter u.

public func normal(at u: Double) -> SIMD2<Double>?
  • Parameters: u — curve parameter.
  • Returns: Unit normal, or nil if undefined (e.g. curvature is zero on a straight line).
  • OCCT: Geom2dLProp_CLProps2d::Normal.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let n = circle.normal(at: 0) {
        print(n)  // ≈ (-1, 0) pointing toward center
    }
    

tangentDirection(at:)

Returns the unit tangent direction at parameter u.

public func tangentDirection(at u: Double) -> SIMD2<Double>?
  • Parameters: u — curve parameter.
  • Returns: Unit tangent, or nil if undefined.
  • OCCT: Geom2dLProp_CLProps2d::Tangent.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let t = circle.tangentDirection(at: 0) {
        print(t)  // ≈ (0, 1)
    }
    

centerOfCurvature(at:)

Returns the center of the osculating circle at parameter u.

public func centerOfCurvature(at u: Double) -> SIMD2<Double>?
  • Parameters: u — curve parameter.
  • Returns: Center of curvature, or nil if curvature is zero (straight segment).
  • OCCT: Geom2dLProp_CLProps2d::CentreOfCurvature.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let coc = circle.centerOfCurvature(at: 0) {
        print(coc)  // ≈ (0, 0) — the center of the circle itself
    }
    

inflectionPoints()

Finds all inflection points (where curvature changes sign) and returns their parameter values.

public func inflectionPoints() -> [Double]

Capped internally at 256 results.

  • Returns: Array of parameter values at inflection points (may be empty).
  • OCCT: GeomLProp_CurAndInf2d::PerformInf.
  • Example:
    if let spline = Curve2D.interpolate(points: pts, startTangent: t1, endTangent: t2) {
        let inflections = spline.inflectionPoints()
    }
    

curvatureExtrema()

Finds local minima and maxima of curvature magnitude.

public func curvatureExtrema() -> [Curve2DSpecialPoint]

Returns Curve2DSpecialPoint values with .minCurvature or .maxCurvature type classification. Capped at 256 results.

  • Returns: Array of special points (may be empty).
  • OCCT: GeomLProp_CurAndInf2d::PerformCurExt.
  • Example:
    if let spline = Curve2D.interpolate(points: pts, startTangent: t1, endTangent: t2) {
        for sp in spline.curvatureExtrema() {
            print(sp.parameter, sp.type)
        }
    }
    

allSpecialPoints()

Returns all special points — both inflection points and curvature extrema — in a single pass.

public func allSpecialPoints() -> [Curve2DSpecialPoint]

Capped internally at 256 results.

  • Returns: Array of Curve2DSpecialPoint values (.inflection, .minCurvature, or .maxCurvature).
  • OCCT: GeomLProp_CurAndInf2d::Perform.
  • Example:
    if let spline = Curve2D.interpolate(points: pts, startTangent: t1, endTangent: t2) {
        let special = spline.allSpecialPoints()
    }
    

Curve2DSpecialPointType

Enum classifying a special point on a curve.

public enum Curve2DSpecialPointType: Int32, Sendable {
    case inflection    = 0
    case minCurvature  = 1
    case maxCurvature  = 2
}
Case Value Meaning
inflection 0 Curvature changes sign at this parameter (a zero-crossing).
minCurvature 1 Local minimum of curvature magnitude.
maxCurvature 2 Local maximum of curvature magnitude.

Curve2DSpecialPointType.maxCurvature

Local maximum of curvature magnitude.


Curve2DSpecialPoint

Struct returned by curvatureExtrema() and allSpecialPoints().

public struct Curve2DSpecialPoint: Sendable {
    public let parameter: Double
    public let type: Curve2DSpecialPointType
}
  • parameter — curve parameter at the special point.
  • type — inflection, minimum curvature, or maximum curvature.

Bounding Box


boundingBox

The axis-aligned bounding box of this curve.

public var boundingBox: (min: SIMD2<Double>, max: SIMD2<Double>)?
  • Returns: Tuple with min and max corners, or nil if the underlying curve has no computable bounding box.
  • OCCT: Geom2dAdaptor_Curve + BndLib_Add2dCurve.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let bb = circle.boundingBox {
        print(bb.min, bb.max)  // ≈ (-5,-5), (5,5)
    }
    

Analysis

General-purpose 2D intersection, projection, and extrema members of Curve2D.


Curve2DIntersection

Struct representing an intersection between two 2D curves.

public struct Curve2DIntersection: Sendable {
    public let point:      SIMD2<Double>
    public let parameter1: Double
    public let parameter2: Double
}
  • point — 2D intersection coordinate.
  • parameter1 / parameter2 — parameters on the first and second curves at the intersection.

(Per-field anchors below, for cross-reference; the list above has the actual meaning of each.)

Curve2DIntersection.parameter2


Curve2DProjection

Struct representing a projection of a point onto a 2D curve.

public struct Curve2DProjection: Sendable {
    public let point:     SIMD2<Double>
    public let parameter: Double
    public let distance:  Double
}
  • point — nearest point on the curve.
  • parameter — curve parameter at that point.
  • distance — distance from the queried point to the curve.

Curve2DExtremaResult

Struct representing a distance extremum between two 2D curves.

public struct Curve2DExtremaResult: Sendable {
    public let pointOnCurve1: SIMD2<Double>
    public let pointOnCurve2: SIMD2<Double>
    public let parameter1:    Double
    public let parameter2:    Double
    public let distance:      Double
}

Curve2DExtremaResult.pointOnCurve1

The extremal point on the first curve.

Curve2DExtremaResult.pointOnCurve2

The extremal point on the second curve.

Curve2DExtremaResult.parameter1

Parameter on the first curve at the extremal point.

Curve2DExtremaResult.parameter2

Parameter on the second curve at the extremal point.


intersections(with:tolerance:)

Finds all intersection points between this curve and another.

public func intersections(with other: Curve2D, tolerance: Double = 1e-6) -> [Curve2DIntersection]

Capped at 128 results.

  • Parameters: other — the curve to intersect with; tolerance — intersection tolerance (default 1e-6).
  • Returns: Array of Curve2DIntersection values (may be empty).
  • OCCT: Geom2dAPI_InterCurveCurve.
  • Example:
    if let c1 = Curve2D.circle(center: .zero, radius: 5),
       let c2 = Curve2D.circle(center: SIMD2(3, 0), radius: 5) {
        let pts = c1.intersections(with: c2)
        // pts.count == 2 for overlapping circles
    }
    

selfIntersections(tolerance:)

Finds all self-intersection points of this curve.

public func selfIntersections(tolerance: Double = 1e-6) -> [Curve2DIntersection]

Capped at 128 results.

  • Parameters: tolerance — intersection tolerance (default 1e-6).
  • Returns: Array of Curve2DIntersection values (may be empty).
  • OCCT: Geom2dAPI_InterCurveCurve self-intersection mode.
  • Example:
    if let figure8 = makeFigureEightCurve() {
        let si = figure8.selfIntersections()
    }
    

project(point:)

Projects a point onto this curve, returning the single nearest projection.

public func project(point p: SIMD2<Double>) -> Curve2DProjection?
  • Parameters: p — 2D point to project.
  • Returns: Nearest Curve2DProjection, always inside the curve’s own domain, or nil when there is no curve to answer about.
  • OCCT: occtNearestPointOnCurve2dRange — the minimum over every Geom2dAPI_ProjectPointOnCurve extremum in range and both curve ends. There is no third source: ShapeAnalysis_Curve has no 2D projection (#615).
  • Note: project(_:) (the Point2D overload), Point2D.distance(to:) and nearestParameter(to:) compute the same nearest solution through the same shared bridge path and agree with it exactly (#413, #615). The answer is the true nearest point rather than the nearest perpendicular foot: a point past the end of a bounded curve is nearest to that end, and a half arc queried from below answers with its near end. Until #615 all of these reported an extremum instead — the far side of that arc, 11 away where the truth is 7.81 — or nil where there was none. allProjections(of:) is deliberately not in the agreement; it asks for the extrema, and still reports none for those points.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let proj = circle.project(point: SIMD2(3, 4)) {
        print(proj.parameter, proj.distance)  // distance ≈ 0 (point is on the circle)
    }
    

allProjections(of:)

Projects a point onto this curve, returning all projection solutions.

public func allProjections(of p: SIMD2<Double>) -> [Curve2DProjection]

Capped at 64 results. Useful when a point has several local-minimum projections — e.g. a point outside a circle projects to both the near and the far side.

  • Parameters: p — 2D point to project.
  • Returns: Array of Curve2DProjection values, empty when there is no extremum at all.
  • OCCT: Geom2dAPI_ProjectPointOnCurve (all solutions).
  • Note: This asks for the extrema — the perpendicular feet — which since #615 is visibly a different question from “the nearest point”. A bounded curve queried from beyond its end has no foot, so this returns empty where project(point:) answers with the end. An extremum may also be a local maximum: on a half arc queried from the far side, the only element here is the point furthest away.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5) {
        let projs = circle.allProjections(of: SIMD2(10, 0))
        // 2 solutions: the near side and the far side
    
        let none = circle.allProjections(of: .zero)
        // empty — the centre is equidistant from every point, so there is no local minimum
    }
    

minDistance(to:)

Finds the minimum distance between this curve and another.

public func minDistance(to other: Curve2D) -> Curve2DExtremaResult?
  • Parameters: other — the curve to measure distance to.
  • Returns: Curve2DExtremaResult for the closest pair of points, or nil on failure.
  • OCCT: Geom2dAPI_ExtremaCurveCurve (minimum solution).
  • Example:
    if let c1 = Curve2D.circle(center: .zero, radius: 3),
       let c2 = Curve2D.circle(center: SIMD2(10, 0), radius: 3),
       let ex = c1.minDistance(to: c2) {
        print(ex.distance)  // ≈ 4.0
    }
    

allExtrema(with:)

Finds all distance extrema (local min and max distances) between this curve and another.

public func allExtrema(with other: Curve2D) -> [Curve2DExtremaResult]

Capped at 64 results.

  • Parameters: other — the curve to compute extrema against.
  • Returns: Array of Curve2DExtremaResult values (may be empty).
  • OCCT: Geom2dAPI_ExtremaCurveCurve (all solutions).
  • Example:
    if let c1 = Curve2D.circle(center: .zero, radius: 3),
       let c2 = Curve2D.circle(center: SIMD2(10, 0), radius: 3) {
        let extrema = c1.allExtrema(with: c2)
        // extrema.count == 2 (closest and farthest pair of points)
    }
    

Batch Evaluation (v0.28.0)


evaluateGrid(_:)

Evaluates the curve at multiple parameter values in a single call.

public func evaluateGrid(_ parameters: [Double]) -> [SIMD2<Double>]

Uses OCCT’s optimised grid evaluator; faster than calling point(at:) repeatedly for dense sampling. This is the canonical batch spelling; evalBatchD0(params:) and gridEvalD0(params:) forwarded here as deprecated aliases (#486) and were removed at v2.0.0 (#784).

  • Parameters: parameters — array of parameter values.
  • Returns: Array of 2D points corresponding to each parameter; empty if parameters is empty.
  • OCCT: Geom2dGridEval_Curve::EvaluateGrid via OCCTCurve2DEvaluateGrid.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5) {
        let params = stride(from: 0.0, through: 2 * .pi, by: 0.01).map { $0 }
        let points = circle.evaluateGrid(params)
    }
    

evaluateGridD1(_:)

Evaluates the curve and its first derivative at multiple parameter values in a single call.

public func evaluateGridD1(_ parameters: [Double]) -> [(point: SIMD2<Double>, tangent: SIMD2<Double>)]
  • Parameters: parameters — array of parameter values.
  • Returns: Array of (point, tangent) tuples; empty if parameters is empty.
  • OCCT: Geom2dGridEval_Curve::EvaluateGridD1 via OCCTCurve2DEvaluateGridD1.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5) {
        let params = [0.0, .pi / 2, .pi, 3 * .pi / 2]
        let results = circle.evaluateGridD1(params)
        for r in results { print(r.point, r.tangent) }
    }
    

Extrema 2D

Elementary 2D curve–curve and point–curve distance solvers (Extrema_ExtElC2d, Extrema_ExtPElC2d, Extrema_ExtCC2d).


Extrema2DResult

Struct representing a single distance extremum between two 2D elements.

public struct Extrema2DResult: Sendable {
    public let squareDistance: Double
    public var distance: Double { squareDistance.squareRoot() }
    public let param1:  Double
    public let param2:  Double
    public let point1:  SIMD2<Double>
    public let point2:  SIMD2<Double>
}
  • squareDistance — squared distance (avoids sqrt cost).
  • distance — computed square root of squareDistance.
  • param1 / param2 — parameters on the first and second elements.
  • point1 / point2 — closest points on each element.

(Per-field anchors below, for cross-reference; the list above has the actual meaning of each.)

Extrema2DResult.point2


Extrema2d.distanceBetweenLines(line1Point:line1Dir:line2Point:line2Dir:tolerance:)

Computes the distance between two 2D lines.

public static func distanceBetweenLines(
    line1Point: SIMD2<Double>, line1Dir: SIMD2<Double>,
    line2Point: SIMD2<Double>, line2Dir: SIMD2<Double>,
    tolerance: Double = 1e-6
) -> (isParallel: Bool, results: [Extrema2DResult])

When lines are parallel, isParallel is true and one result with the perpendicular distance is returned.

  • Returns: (isParallel, results) — when parallel: one result with distance; when intersecting: one result with distance zero.
  • OCCT: Extrema_ExtElC2d (line–line).
  • Example:
    let r = Extrema2d.distanceBetweenLines(
        line1Point: .zero, line1Dir: SIMD2(1, 0),
        line2Point: SIMD2(0, 5), line2Dir: SIMD2(1, 0))
    // r.isParallel == true, r.results.first?.distance ≈ 5
    

Extrema2d.distanceBetweenLineAndCircle(linePoint:lineDir:circleCenter:circleRadius:tolerance:)

Computes distance extrema between a 2D line and a 2D circle.

public static func distanceBetweenLineAndCircle(
    linePoint: SIMD2<Double>, lineDir: SIMD2<Double>,
    circleCenter: SIMD2<Double>, circleRadius: Double,
    tolerance: Double = 1e-6
) -> [Extrema2DResult]
  • Returns: Array of extrema results (min and/or max distance points; may be empty on failure).
  • OCCT: Extrema_ExtElC2d (line–circle).
  • Note: circleRadius must be positive (#553). With a radius of zero the extrema come back correct but duplicated; distanceFromPointToLine(point:linePoint:lineDir:) answers the point question directly. A non-positive radius returns an empty array.
  • Example:
    let extrema = Extrema2d.distanceBetweenLineAndCircle(
        linePoint: SIMD2(0, 10), lineDir: SIMD2(1, 0),
        circleCenter: .zero, circleRadius: 5)
    

Extrema2d.distanceFromPointToCircle(point:circleCenter:circleRadius:tolerance:)

Returns the closest and farthest points on a 2D circle from a given point.

public static func distanceFromPointToCircle(
    point: SIMD2<Double>,
    circleCenter: SIMD2<Double>, circleRadius: Double,
    tolerance: Double = 1e-6
) -> [Extrema2DResult]
  • Returns: Array of up to 2 results (min and max distance to the circle).
  • OCCT: Extrema_ExtPElC2d (point–circle).
  • Note: circleRadius must be positive (#553). This is the family where a zero radius loses the answer outright: measured, OCCT reports no extremum at all rather than the distance to the centre. A non-positive radius returns an empty array.
  • Example:
    let extrema = Extrema2d.distanceFromPointToCircle(
        point: SIMD2(10, 0), circleCenter: .zero, circleRadius: 5)
    // extrema[0].distance ≈ 5 (near), extrema[1].distance ≈ 15 (far)
    

Extrema2d.distanceFromPointToLine(point:linePoint:lineDir:tolerance:)

Returns the closest point on a 2D line from a given point.

public static func distanceFromPointToLine(
    point: SIMD2<Double>,
    linePoint: SIMD2<Double>, lineDir: SIMD2<Double>,
    tolerance: Double = 1e-6
) -> [Extrema2DResult]
  • Returns: Array with one result (the foot of the perpendicular from the point to the line).
  • OCCT: Extrema_ExtPElC2d (point–line).
  • Example:
    let r = Extrema2d.distanceFromPointToLine(
        point: SIMD2(3, 4),
        linePoint: .zero, lineDir: SIMD2(1, 0))
    // r.first?.distance ≈ 4
    

Extrema2d.distanceBetweenCurves(_:first1:last1:_:first2:last2:)

Finds all distance extrema between two arbitrary 2D curves within given parameter ranges.

public static func distanceBetweenCurves(
    _ c1: Curve2D, first1: Double, last1: Double,
    _ c2: Curve2D, first2: Double, last2: Double
) -> [Extrema2DResult]

Capped at 32 results.

  • Parameters: c1/c2 — the two curves; first1/last1 — parameter range on c1; first2/last2 — parameter range on c2.
  • Returns: Array of Extrema2DResult values for all local extrema (may be empty).
  • OCCT: Extrema_ExtCC2d.
  • Example:
    if let c1 = Curve2D.circle(center: .zero, radius: 3),
       let c2 = Curve2D.circle(center: SIMD2(8, 0), radius: 2) {
        let ex = Extrema2d.distanceBetweenCurves(c1, first1: 0, last1: 2 * .pi,
                                                 c2, first2: 0, last2: 2 * .pi)
    }
    

Geom2dLProp: Curvature Inflection/Extrema

CurInfPoint/CurInfType are an alternate vocabulary for the same curvatureExtrema()/ inflectionPoints() results above — same GeomLProp_CurAndInf2d computation, no second solver run. curvatureExtremaDetailed()/inflectionPointsDetailed() delegate to those two functions and translate each Curve2DSpecialPoint into a CurInfPoint via CurInfType.init(_:).


CurInfType

Enum classifying a curvature feature point. Numbers the same 3 cases as Curve2DSpecialPointType in a different order; CurInfType.init(_:) is the pinned mapping between them (see the Geom2dLProp Curvature Analysis test suite for the parity tests).

public enum CurInfType: Int32, Sendable {
    case curvatureMinimum = 0
    case curvatureMaximum = 1
    case inflection       = 2
}
Case Meaning
curvatureMinimum A local minimum of curvature (same feature curvatureExtrema() reports).
curvatureMaximum A local maximum of curvature.
inflection An inflection point (curvature crosses zero, same feature inflectionPoints() reports).

(Per-case anchors below, for cross-reference; the table above has the actual meaning of each.)

CurInfType.inflection


CurInfPoint

Struct returned by curvatureExtremaDetailed() and inflectionPointsDetailed().

public struct CurInfPoint: Sendable {
    public let parameter: Double
    public let type:      CurInfType
}

curvatureExtremaDetailed()

Finds local curvature extrema with min/max type classification.

public func curvatureExtremaDetailed() -> [CurInfPoint]

Unlike curvatureExtrema() (which returns Curve2DSpecialPoint), this returns CurInfPoint values using the CurInfType enum — the two share one underlying GeomLProp_CurAndInf2d call, so their parameter/count results always agree. Capped at 256 results (the shared function’s cap).

  • Returns: Array of CurInfPoint with .curvatureMinimum or .curvatureMaximum type.
  • OCCT: GeomLProp_CurAndInf2d::PerformCurExt, via curvatureExtrema().
  • Example:
    if let spline = Curve2D.interpolate(points: pts, startTangent: t1, endTangent: t2) {
        for pt in spline.curvatureExtremaDetailed() {
            print(pt.parameter, pt.type)
        }
    }
    

inflectionPointsDetailed()

Finds inflection points with type information.

public func inflectionPointsDetailed() -> [CurInfPoint]

Like inflectionPoints() but returns CurInfPoint values (all with .inflection type) rather than bare Double parameters — delegates directly to it, so results always match. Capped at 256 results (the shared function’s cap).

  • Returns: Array of CurInfPoint (all .inflection).
  • OCCT: GeomLProp_CurAndInf2d::PerformInf, via inflectionPoints().
  • Example:
    if let spline = Curve2D.interpolate(points: pts, startTangent: t1, endTangent: t2) {
        let infl = spline.inflectionPointsDetailed()
    }
    

IntAna2d Analytical Intersections

Exact (closed-form) intersection between elementary 2D curves. All methods return [Intersection2DPoint].


Intersection2DPoint

Struct representing an analytical 2D intersection result.

public struct Intersection2DPoint: Sendable {
    public let point:  SIMD2<Double>
    public let param1: Double
    public let param2: Double
}
  • point — 2D coordinates of the intersection.
  • param1 / param2 — parameters on each input element at the intersection.

IntAna2d.intersectLines(line1Point:line1Dir:line2Point:line2Dir:)

Intersects two 2D lines analytically.

public static func intersectLines(
    line1Point: SIMD2<Double>, line1Dir: SIMD2<Double>,
    line2Point: SIMD2<Double>, line2Dir: SIMD2<Double>
) -> [Intersection2DPoint]

Returns 0 results for parallel lines, 1 result for a transverse intersection.

  • Returns: Array of Intersection2DPoint (0 or 1 elements).
  • OCCT: IntAna2d_AnaIntersection (line–line).
  • Example:
    let pts = IntAna2d.intersectLines(
        line1Point: .zero, line1Dir: SIMD2(1, 0),
        line2Point: .zero, line2Dir: SIMD2(0, 1))
    // pts.count == 1, pts[0].point ≈ (0, 0)
    

IntAna2d.intersectLineCircle(linePoint:lineDir:circleCenter:circleRadius:)

Intersects a 2D line and a circle analytically.

public static func intersectLineCircle(
    linePoint: SIMD2<Double>, lineDir: SIMD2<Double>,
    circleCenter: SIMD2<Double>, circleRadius: Double
) -> [Intersection2DPoint]

Returns 0, 1 (tangent), or 2 intersection points.

  • Returns: Array of 0–2 Intersection2DPoint values.
  • OCCT: IntAna2d_AnaIntersection (line–circle).
  • Note: circleRadius must be positive (#553). A zero-radius circle is a point, and whether a point lies on a line is not an intersection query: measured, OCCT answers with the centre and a param2 of NaN. A non-positive radius returns an empty array.
  • Example:
    let pts = IntAna2d.intersectLineCircle(
        linePoint: SIMD2(0, -10), lineDir: SIMD2(0, 1),
        circleCenter: .zero, circleRadius: 5)
    // pts.count == 2 (chord through circle)
    

IntAna2d.intersectCircles(center1:radius1:center2:radius2:)

Intersects two 2D circles analytically.

public static func intersectCircles(
    center1: SIMD2<Double>, radius1: Double,
    center2: SIMD2<Double>, radius2: Double
) -> [Intersection2DPoint]

Returns 0 (disjoint or concentric), 1 (tangent), or 2 intersection points.

  • Returns: Array of 0–2 Intersection2DPoint values.
  • OCCT: IntAna2d_AnaIntersection (circle–circle).
  • Note: both radii must be positive, for the same reason as intersectLineCircle (#553). A non-positive radius returns an empty array.
  • Example:
    let pts = IntAna2d.intersectCircles(
        center1: .zero, radius1: 5,
        center2: SIMD2(6, 0), radius2: 5)
    // pts.count == 2
    

ShapeCustom_Curve2d & Approx_Curve2d (v0.52.0)

Curve simplification, linearity detection, and BSpline approximation from ShapeCustom_Curve2d and Approx_Curve2d.


isLinear(tolerance:)

Checks whether this 2D BSpline curve has nearly collinear control points.

public func isLinear(tolerance: Double = 1e-6) -> (isLinear: Bool, deviation: Double)?
  • Parameters: tolerance — maximum allowed deviation from a straight line.
  • Returns: Tuple (isLinear, deviation) where deviation is the actual maximum deviation, or nil if the curve is not a BSpline.
  • OCCT: ShapeCustom_Curve2d::IsLinear.
  • Example:
    if let seg = Curve2D.segment(from: SIMD2(0, 0), to: SIMD2(10, 0)),
       let bsp = seg.toBSpline(),
       let check = bsp.isLinear() {
        print(check.isLinear, check.deviation)  // true, ≈ 0
    }
    

convertToLine(first:last:tolerance:)

Converts a nearly-linear 2D curve to a line within the given parameter range.

public func convertToLine(
    first: Double, last: Double, tolerance: Double = 1e-3
) -> (line: Curve2D, newFirst: Double, newLast: Double, deviation: Double)?

Returns the equivalent line curve along with reparametrized bounds, or nil if the curve is not within tolerance of a line.

  • Parameters: first/last — parameter range to check; tolerance — deviation tolerance.
  • Returns: Tuple (line, newFirst, newLast, deviation), or nil if not linear within tolerance.
  • OCCT: ShapeCustom_Curve2d::ConvertToLine.
  • Example:
    if let bsp = someCurve.toBSpline(),
       let result = bsp.convertToLine(first: 0, last: 1) {
        let line = result.line
    }
    

simplifyBSpline(tolerance:)

Removes unnecessary knots from a 2D BSpline in place.

@discardableResult
public func simplifyBSpline(tolerance: Double = 1e-6) -> Bool

Mutates the receiver. Returns true if any knots were removed.

  • Parameters: tolerance — maximum allowed shape deviation after removal.
  • Returns: true if simplification occurred.
  • OCCT: ShapeCustom_Curve2d::SimplifyBSpline.
  • Example:
    if let bsp = someCurve.toBSpline() {
        bsp.simplifyBSpline(tolerance: 1e-4)
    }
    

approximatedInRange(first:last:toleranceU:toleranceV:maxDegree:maxSegments:)

Approximates an explicit parameter sub-range of this 2D curve as a BSpline, with independent U/V tolerances.

public func approximatedInRange(
    first: Double, last: Double,
    toleranceU: Double = 1e-6, toleranceV: Double = 1e-6,
    maxDegree: Int = 8, maxSegments: Int = 100
) -> Curve2D?

Not a ranged overload of approximated(tolerance:continuity:maxSegments:maxDegree:) — the two wrap different OCCT algorithms (Approx_Curve2d here vs. Geom2dConvert_ApproxCurve there) with different tolerance semantics, so their default tolerances (1e-6 here, 1e-3 there — a 1000x gap) are not directly comparable: this one bounds independent per-axis error on a restricted range, the other bounds a single whole-curve error. Continuity is fixed at C2 here and is not caller-configurable; use the other overload if you need a different continuity order. See #407.

Renamed from approximated(first:last:toleranceU:toleranceV:maxDegree:maxSegments:). The old spelling still compiles — it’s kept as an @available(*, deprecated, renamed:) shim forwarding directly to this method, so existing call sites get a migration warning rather than a hard break.

  • Parameters: first/last — parameter sub-range to approximate; toleranceU/toleranceV — per-axis approximation tolerances; maxDegree — maximum polynomial degree (default 8); maxSegments — maximum number of segments (default 100).
  • Returns: Approximated BSpline Curve2D, or nil on failure.
  • OCCT: Approx_Curve2d.
  • Example:
    if let approx = someCurve.approximatedInRange(first: 0, last: 1, toleranceU: 1e-4) {
        print(approx.degree)
    }
    

v0.115.0: Interpolation expansion, trim, length


interpolate(points:startTangent:endTangent:tolerance:)

Interpolates a 2D BSpline through a sequence of points with prescribed endpoint tangents.

public static func interpolate(
    points: [SIMD2<Double>],
    startTangent: SIMD2<Double>,
    endTangent: SIMD2<Double>,
    tolerance: Double = 1e-6
) -> Curve2D?

A spelling of interpolate(through:startTangent:endTangent:tolerance:) with the points: argument label, and it delegates to it — the two cannot produce different curves for the same input. Before #410 it was a second, independent Geom2dAPI_Interpolate call site with the tolerance hardcoded at 1e-6 and no parameter to change it.

  • Parameters: points — interpolation points; startTangent/endTangent — tangent directions at the first and last point; tolerance — interpolation tolerance (default 1e-6, matching the value this method used to hardcode).
  • Returns: Interpolated BSpline, or nil on failure.
  • OCCT: Geom2dAPI_Interpolate (with tangent constraints).
  • Example:
    let pts: [SIMD2<Double>] = [.zero, SIMD2(5, 3), SIMD2(10, 0)]
    if let curve = Curve2D.interpolate(
        points: pts,
        startTangent: SIMD2(1, 0),
        endTangent: SIMD2(1, 0)) {
        print(curve.domain)
    }
    

interpolatePeriodic(points:tolerance:)

Interpolates a closed (periodic) 2D BSpline through a sequence of points.

public static func interpolatePeriodic(points: [SIMD2<Double>],
                                       tolerance: Double = 1e-6) -> Curve2D?

A spelling of interpolate(through:closed:tolerance:) with closed: true, and it delegates to it — the two cannot produce different curves for the same input. Before #412 it was a second, independent Geom2dAPI_Interpolate call site with the tolerance pinned at 1e-6 and unreachable, and with a stricter minimum point count (3, against the general entry point’s 2) that the two had drifted into.

  • Parameters: points — interpolation points, minimum 2 (do not repeat the first point at the end; the curve closes automatically); tolerance — point coincidence tolerance.
  • Returns: Periodic BSpline, or nil on failure.
  • OCCT: Geom2dAPI_Interpolate (periodic).
  • Example:
    let pts: [SIMD2<Double>] = [SIMD2(5, 0), SIMD2(0, 5), SIMD2(-5, 0), SIMD2(0, -5)]
    if let loop = Curve2D.interpolatePeriodic(points: pts) {
        print(loop.isClosed)
    }
    

approximate(points:degMin:degMax:continuity:tolerance:)

Approximates (fits) a 2D BSpline through a set of points with degree and continuity control.

public static func approximate(
    points: [SIMD2<Double>],
    degMin: Int = 3, degMax: Int = 8,
    continuity: Int = 2, tolerance: Double = 1e-3
) -> Curve2D?
  • Parameters: points — sample points to approximate; degMin/degMax — degree range; continuity — desired continuity (0=C0, 1=C1, 2=C2); tolerance — maximum fitting error.
  • Returns: Approximated BSpline, or nil on failure.
  • OCCT: Geom2dAPI_PointsToBSpline.
  • Example:
    let pts = (0..<20).map { i -> SIMD2<Double> in
        let t = Double(i) / 19 * 2 * .pi
        return SIMD2(cos(t) * 5, sin(t) * 3)
    }
    if let ellipseApprox = Curve2D.approximate(points: pts) {
        print(ellipseApprox.degree)
    }
    

arcLength(from:to:)

Computes the arc length of this curve between two parameter values (non-optional). Delegates to length(from:to:), the failure-distinguishing entry point, and shares its contract: either parameter order, 0 for equal parameters, and a range outside the curve’s domain measuring only the part that lies on the curve (winding a periodic one, #600). Curve3D.arcLength(from:to:) has had the same shape since #408.

public func arcLength(from u1: Double, to u2: Double) -> Double
  • Parameters: u1/u2: parameter range, in either order. Both must be finite.
  • Returns: Arc length value, or -1.0 on failure (e.g. a non-finite bound). Arc length is otherwise always non-negative, so -1.0 is unambiguous and never collides with a genuine zero-length result (e.g. u1 == u2). Use length(from:to:) directly if you need an optional.
  • OCCT: GCPnts_AbscissaPoint::Length(adaptor, u1, u2), subdivided per GeomAbs_CN interval, the same measurement as length(from:to:) (#603).
  • Note: .nan and ±.infinity return -1.0 rather than propagating into the result. The pre-#548 unranged path could return +infinity for an infinite bound on a segment or a circle, which passed the bridge’s non-negative check unnoticed (#548).
  • Note: A range reaching outside the curve’s domain measures only the part that lies on the curve, matching length(from:to:). The pre-bounded adaptor this used to delegate to (see History) evaluated the curve past its domain instead: 4771.88 for a BSpline 457.26 long (#600).
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5) {
        let halfCircumference = circle.arcLength(from: 0, to: .pi)   // ≈ 15.71
        let same = circle.arcLength(from: .pi, to: 0)                // the same 15.71
    }
    
  • History: until #549 this measured through Geom2dAdaptor_Curve(curve, u1, u2), a range-checked constructor that reported a reversed range as -1.0 and extrapolated past a multi-span curve’s knots (8082 for a curve 353.5 long). #506 removed the same adaptor from the 3D path. #600 then fixed the same extrapolation-past-domain defect on the surviving delegate.

splitAtContinuity(continuity:tolerance:maxSegments:)

Splits this curve at discontinuities of the requested continuity level.

public func splitAtContinuity(
    continuity: Int = 1, tolerance: Double = 1e-6,
    maxSegments: Int = 32
) -> [Curve2D]
  • Parameters: continuity — 0=C0, 1=C1, 2=C2; tolerance — detection tolerance; maxSegments — output capacity (default 32), clamped into 0...Sampling.maximumSampleCount (10,000,000); 0 or less returns empty (#622).
  • Returns: Array of sub-curves (one per continuous segment); may be empty if the curve has no discontinuities or the split fails.
  • OCCT: Geom2dConvert::C0BSplineToArrayOfC1BSplineCurve and related splitting utilities.
  • Example:
    if let composite = Curve2D.join([seg1, seg2, seg3]) {
        let pieces = composite.splitAtContinuity(continuity: 1)
    }
    

v0.80.0: Extrema, gce factories, GeomTools persistence

Local extrema search, factory construction from gce_Make* classes, and serialization via GeomTools_Curve2dSet.


LocalExtrema2dResult

Struct returned by locateExtremaCC(range1:other:range2:seedU:seedV:).

public struct LocalExtrema2dResult: Sendable {
    public let isDone:         Bool
    public let squareDistance: Double
    public let point1:         SIMD2<Double>
    public let param1:         Double
    public let point2:         SIMD2<Double>
    public let param2:         Double
}
  • isDonetrue if a local extremum was found near the seed parameters.
  • squareDistance — squared distance at the local extremum.
  • point1/point2 — closest points on each curve.
  • param1/param2 — parameters at those points.
Field Meaning
isDone true if a local extremum was found near the seed parameters.
squareDistance Squared distance at the local extremum.
point1 Closest point on self, the curve locateExtremaCC was called on.
param1 Parameter on self at point1.
point2 Closest point on other, the curve passed to locateExtremaCC.
param2 Parameter on other at point2.

Curve2D.LocalExtrema2dResult.param2

Parameter on other at point2.


locateExtremaCC(range1:other:range2:seedU:seedV:)

Finds a local curve–curve extremum near given seed parameters using Extrema_LocateExtCC2d.

public func locateExtremaCC(
    range1: ClosedRange<Double>? = nil,
    other: Curve2D,
    range2: ClosedRange<Double>? = nil,
    seedU: Double, seedV: Double
) -> LocalExtrema2dResult

When range1/range2 are nil, the curve’s full domain is used. Useful for finding a specific local minimum when the approximate location is known.

  • Parameters: range1/range2 — optional parameter ranges on self and other; seedU/seedV — initial parameter guesses on self and other.
  • Returns: LocalExtrema2dResult (check isDone before using distance/point fields).
  • OCCT: Extrema_LocateExtCC2d.
  • Example:
    if let c1 = Curve2D.circle(center: .zero, radius: 3),
       let c2 = Curve2D.circle(center: SIMD2(8, 0), radius: 2) {
        let r = c1.locateExtremaCC(other: c2, seedU: 0, seedV: .pi)
        if r.isDone { print(r.squareDistance.squareRoot()) }
    }
    

circleFromCenterRadius(center:radius:)

Creates a 2D circle from a center point and radius using gce_MakeCirc2d.

public static func circleFromCenterRadius(center: SIMD2<Double>, radius: Double) -> Curve2D?
  • Parameters: center — center point; radius — radius (must be > 0).
  • Returns: Curve2D (circle), or nil if radius ≤ 0.
  • OCCT: gce_MakeCirc2d (center + radius constructor).
  • Note: Geometrically identical to circle(center:radius:), and enforces the same radius precondition. gce_MakeCirc2d itself accepts Radius >= 0, so the bridge adds the zero check to keep the two factories in agreement — before #411 this page documented the radius ≤ 0 rejection that only the direct factory actually performed.
  • Example:
    if let c = Curve2D.circleFromCenterRadius(center: SIMD2(1, 2), radius: 4) {
        print(c.circleProperties.radius)  // 4.0
    }
    

circleThrough3Points(_:_:_:)

Creates a 2D circle through three points using gce_MakeCirc2d.

public static func circleThrough3Points(
    _ p1: SIMD2<Double>, _ p2: SIMD2<Double>, _ p3: SIMD2<Double>
) -> Curve2D?
  • Parameters: p1, p2, p3 — three non-collinear points.
  • Returns: Curve2D (circle), or nil if points are collinear or coincident.
  • OCCT: gce_MakeCirc2d (3-point constructor).
  • Example:
    if let c = Curve2D.circleThrough3Points(SIMD2(5,0), SIMD2(0,5), SIMD2(-5,0)) {
        print(c.circleProperties.center)  // ≈ (0, 0)
    }
    

lineFrom2Points(_:_:)

Creates a 2D line through two points using gce_MakeLin2d.

public static func lineFrom2Points(_ p1: SIMD2<Double>, _ p2: SIMD2<Double>) -> Curve2D?
  • Parameters: p1, p2 — two distinct points.
  • Returns: Curve2D (infinite line), or nil if points coincide.
  • OCCT: gce_MakeLin2d (2-point constructor).
  • Example:
    if let line = Curve2D.lineFrom2Points(SIMD2(0, 0), SIMD2(1, 1)) {
        print(line.lineProperties.direction)  // ≈ (0.707, 0.707)
    }
    

lineFromEquation(a:b:c:)

Creates a 2D line from the equation Ax + By + C = 0 using gce_MakeLin2d.

public static func lineFromEquation(a: Double, b: Double, c: Double) -> Curve2D?
  • Parameters: a, b, c — line equation coefficients.
  • Returns: Curve2D (infinite line), or nil on failure (e.g. a == 0 && b == 0).
  • OCCT: gce_MakeLin2d (equation constructor).
  • Example:
    // y = 2 → 0·x + 1·y − 2 = 0
    if let line = Curve2D.lineFromEquation(a: 0, b: 1, c: -2) {
        print(line.lineProperties.location)  // ≈ (0, 2)
    }
    

ellipseFromCenterDir(center:direction:majorRadius:minorRadius:)

Creates a 2D ellipse from center, major-axis direction, and semi-radii using gce_MakeElips2d.

public static func ellipseFromCenterDir(
    center: SIMD2<Double>, direction: SIMD2<Double>,
    majorRadius: Double, minorRadius: Double
) -> Curve2D?
  • Parameters: center — center point; direction — unit direction of the major axis; majorRadius/minorRadius — semi-axes (both must be > 0, and minorRadius no larger than majorRadius).
  • Returns: Curve2D (ellipse), or nil if either radius is non-positive or minorRadius exceeds majorRadius.
  • OCCT: gce_MakeElips2d.
  • Note: Geometrically identical to ellipse(center:majorRadius:minorRadius:rotation:), which takes the major-axis direction as a rotation angle, and since #487 enforces the same radius precondition. This page documented the rejection before #487, but only the direct factory performed it: gce_MakeElips2d accepted zero radii and, for majorRadius: 5, minorRadius: -3, returned an ellipse reporting a minor radius of -3. Equal radii remain valid.
  • Example:
    if let e = Curve2D.ellipseFromCenterDir(
        center: .zero, direction: SIMD2(1, 0),
        majorRadius: 5, minorRadius: 3) {
        print(e.ellipseProperties.majorRadius)
    }
    // Degenerate dimensions are rejected, matching the direct factory.
    Curve2D.ellipseFromCenterDir(center: .zero, direction: SIMD2(1, 0),
                                 majorRadius: 0, minorRadius: 0)  // nil
    

hyperbolaFromCenterDir(center:direction:majorRadius:minorRadius:)

Creates a 2D hyperbola from center, direction, and semi-radii using gce_MakeHypr2d.

public static func hyperbolaFromCenterDir(
    center: SIMD2<Double>, direction: SIMD2<Double>,
    majorRadius: Double, minorRadius: Double
) -> Curve2D?
  • Parameters: center — center; direction — unit direction of the real axis; majorRadius/minorRadius — real and imaginary semi-axes (both must be > 0, in either order).
  • Returns: Curve2D (hyperbola), or nil if either radius is non-positive.
  • OCCT: gce_MakeHypr2d.
  • Note: Geometrically identical to hyperbola(center:majorRadius:minorRadius:rotation:), which takes the real-axis direction as a rotation angle, and since #487 enforces the same radius precondition. OCCT itself accepts zero radii through both routes, so the rejection is the bridge’s contract, not OCCT’s. Unlike an ellipse, a hyperbola puts no ordering on its radii: a minor radius larger than the major is an ordinary hyperbola and is accepted.
  • Example:
    if let h = Curve2D.hyperbolaFromCenterDir(
        center: .zero, direction: SIMD2(1, 0),
        majorRadius: 4, minorRadius: 3) {
        print(h.hyperbolaProperties.eccentricity)
    }
    // Degenerate dimensions are rejected, matching the direct factory.
    Curve2D.hyperbolaFromCenterDir(center: .zero, direction: SIMD2(1, 0),
                                   majorRadius: 6, minorRadius: 0)  // nil
    

parabolaFromCenterDir(center:direction:focal:)

Creates a 2D parabola from center, axis direction, and focal distance using gce_MakeParab2d.

public static func parabolaFromCenterDir(
    center: SIMD2<Double>, direction: SIMD2<Double>,
    focal: Double
) -> Curve2D?
  • Parameters: center — vertex of the parabola; direction — axis direction; focal — focal distance (must be > 0).
  • Returns: Curve2D (parabola), or nil if focal ≤ 0.
  • OCCT: gce_MakeParab2d.
  • Note: Places the same curve as parabola(focus:direction:focalLength:) once that factory’s focus is set to center + direction * focal, and since #487 enforces the same focal-length precondition. OCCT itself accepts focal == 0 through both routes (gp_Parab2d documents the result as a line parallel to the axis of symmetry), so the rejection is the bridge’s contract, not OCCT’s.
  • Example:
    if let p = Curve2D.parabolaFromCenterDir(
        center: .zero, direction: SIMD2(1, 0), focal: 2) {
        print(p.parabolaProperties.focal)  // 2.0
    }
    // A zero focal length is a line, not a parabola.
    Curve2D.parabolaFromCenterDir(center: .zero, direction: SIMD2(1, 0), focal: 0)  // nil
    

serializeCurves(_:)

Serializes an array of 2D curves to a string using GeomTools_Curve2dSet.

public static func serializeCurves(_ curves: [Curve2D]) -> String?

The resulting string can be stored to disk or passed across process boundaries, then deserialized with deserializeCurves(_:).

  • Parameters: curves — array of curves to serialize.
  • Returns: Serialized string, or nil on failure.
  • OCCT: GeomTools_Curve2dSet::Write.
  • Example:
    if let c = Curve2D.circle(center: .zero, radius: 5),
       let data = Curve2D.serializeCurves([c]) {
        try? data.write(toFile: "/tmp/curves.dat", atomically: true, encoding: .utf8)
    }
    

deserializeCurves(_:)

Deserializes an array of 2D curves from a string produced by serializeCurves(_:).

public static func deserializeCurves(_ data: String) -> [Curve2D]?
  • Parameters: data — serialized curve string.
  • Returns: Array of restored Curve2D values, or nil if the string is invalid or empty.
  • OCCT: GeomTools_Curve2dSet::Read.
  • Example:
    if let data = try? String(contentsOfFile: "/tmp/curves.dat", encoding: .utf8),
       let curves = Curve2D.deserializeCurves(data) {
        print(curves.count)
    }
    

FairCurve

Energy-minimising curves (FairCurve_Batten, FairCurve_MinimalVariation) that model the elastic behaviour of a physical spline.


FairCurveCode

Enum indicating the convergence status of a fair-curve computation.

public enum FairCurveCode: Int32, Sendable {
    case ok               = 0
    case notConverged     = 1
    case infiniteSliding  = 2
    case nullHeight       = 3
}

Case meanings, from FairCurve_AnalysisCode:

FairCurveCode.notConverged

The algorithm did not converge; the result’s quality is not certain, and computation should be resumed before using the curve.

FairCurveCode.infiniteSliding

Sliding is infinite, so computation stopped. Resolve by using an imposed sliding value instead.

FairCurveCode.nullHeight

No matter is left at one of the curve’s ends, so computation stopped. Resolve by increasing or decreasing the slope value.


fairCurveBatten(p1:p2:height:slope:angle1:angle2:constraintOrder1:constraintOrder2:freeSliding:)

Creates a fair curve (batten) of minimal bending energy between two 2D points.

public static func fairCurveBatten(
    p1: SIMD2<Double>, p2: SIMD2<Double>,
    height: Double = 1.0, slope: Double = 0.0,
    angle1: Double = 0.0, angle2: Double = 0.0,
    constraintOrder1: Int = 1, constraintOrder2: Int = 1,
    freeSliding: Bool = true
) -> (curve: Curve2D, code: FairCurveCode)?

constraintOrder controls what is constrained at each endpoint: 0 = position only, 1 = position + tangent, 2 = position + tangent + curvature.

  • Parameters: p1/p2 — endpoints; height — cross-section height; slope — slope parameter; angle1/angle2 — tangent angle constraints (radians); constraintOrder1/constraintOrder2 — constraint orders; freeSliding — whether the batten can slide freely.
  • Returns: (curve, code) tuple, or nil on internal failure. Check code == .ok before trusting the curve.
  • OCCT: FairCurve_Batten::Compute.
  • Example:
    if let result = Curve2D.fairCurveBatten(
        p1: .zero, p2: SIMD2(10, 0),
        height: 0.5, angle1: .pi / 6, angle2: -.pi / 6) {
        if result.code == .ok {
            print(result.curve.domain)
        }
    }
    
  • Note: Returns nil (not .notConverged) only when the bridge itself fails; .notConverged is returned inside the tuple with a partially-computed curve.

fairCurveMinimalVariation(p1:p2:height:slope:angle1:angle2:constraintOrder1:constraintOrder2:freeSliding:physicalRatio:curvature1:curvature2:)

Creates a fair curve with minimal curvature variation between two 2D points.

public static func fairCurveMinimalVariation(
    p1: SIMD2<Double>, p2: SIMD2<Double>,
    height: Double = 1.0, slope: Double = 0.0,
    angle1: Double = 0.0, angle2: Double = 0.0,
    constraintOrder1: Int = 1, constraintOrder2: Int = 1,
    freeSliding: Bool = true,
    physicalRatio: Double = 0.0,
    curvature1: Double = 0.0, curvature2: Double = 0.0
) -> (curve: Curve2D, code: FairCurveCode)?

physicalRatio blends between pure batten (0) and minimal-variation (1) behaviour. Curvature constraints are active only when constraintOrder >= 2.

  • Parameters: p1/p2 — endpoints; height/slope — physical parameters; angle1/angle2 — tangent angles; constraintOrder1/constraintOrder2 — constraint orders; freeSliding; physicalRatio — 0–1 blend; curvature1/curvature2 — endpoint curvatures (used when order ≥ 2).
  • Returns: (curve, code) tuple, or nil on internal failure.
  • OCCT: FairCurve_MinimalVariation::Compute.
  • Example:
    if let result = Curve2D.fairCurveMinimalVariation(
        p1: .zero, p2: SIMD2(10, 0),
        physicalRatio: 0.5) {
        if result.code == .ok {
            let pts = result.curve.evaluateGrid([0, 0.25, 0.5, 0.75, 1.0])
        }
    }
    

Point2D Integration

Bridge between Curve2D and the Point2D type for point-based construction and projection.


pointAt(_:)

Evaluates the curve at parameter t, returning a Point2D.

public func pointAt(_ t: Double) -> Point2D?
  • Parameters: t — curve parameter.
  • Returns: Point2D at parameter t, or nil on failure.
  • OCCT: Geom2d_Curve::Value via Point2D bridge.
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let pt = circle.pointAt(0) {
        print(pt)
    }
    

segment(from:to:) (Point2D overload)

Creates a line segment between two Point2D instances.

public static func segment(from p1: Point2D, to p2: Point2D) -> Curve2D?

Distinct from segment(from:to:) taking SIMD2<Double> parameters.

  • Parameters: p1/p2Point2D endpoints.
  • Returns: Curve2D (trimmed line segment), or nil on failure.
  • OCCT: GCE2d_MakeSegment.
  • Example:
    if let a = someEdge.startPoint2D,
       let b = someEdge.endPoint2D,
       let seg = Curve2D.segment(from: a, to: b) {
        print(seg.domain)
    }
    

project(_:) (Point2D overload)

Projects a Point2D onto this curve.

public func project(_ point: Point2D) -> (parameter: Double, distance: Double)?
  • Parameters: point — the Point2D to project.
  • Returns: (parameter, distance) tuple, or nil when there is no curve to answer about.
  • OCCT: occtNearestPointOnCurve2dRange, shared with project(point:), so it reports the true nearest point over the curve’s own domain — a point past the end answers with that end (#615).
  • Note: A parameter of 0 is an ordinary success — projecting a segment’s own start point onto it returns exactly that — so nil is the only failure signal. The underlying bridge function used to return 0 on failure too, conflating the two (#413).
  • Example:
    if let circle = Curve2D.circle(center: .zero, radius: 5),
       let pt = circle.pointAt(.pi / 4),
       let proj = circle.project(pt) {
        print(proj.parameter, proj.distance)  // distance ≈ 0
    }