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) · Bounding Box · Analysis · Batch Evaluation (v0.28.0) · Extrema 2D · Geom2dLProp: Curvature Inflection/Extrema · IntAna2d Analytical Intersections · ShapeCustom_Curve2d & Approx_Curve2d (v0.52.0) · v0.115.0: Interpolation expansion, trim, length · v0.80.0: Extrema, gce factories, GeomTools persistence · FairCurve · Point2D Integration
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,
0for a straight segment, ornilwhere the curve has no tangent there (a Bezier whose control points all coincide) or the parameter cannot be evaluated. A cusp still reportsDouble.greatestFiniteMagnitude(OCCT’sRealLast(), 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
nilif 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
nilif 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
nilif 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
Curve2DSpecialPointvalues (.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
minandmaxcorners, ornilif 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 (default1e-6). - Returns: Array of
Curve2DIntersectionvalues (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 (default1e-6). - Returns: Array of
Curve2DIntersectionvalues (may be empty). - OCCT:
Geom2dAPI_InterCurveCurveself-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, ornilwhen there is no curve to answer about. - OCCT:
occtNearestPointOnCurve2dRange— the minimum over everyGeom2dAPI_ProjectPointOnCurveextremum in range and both curve ends. There is no third source:ShapeAnalysis_Curvehas no 2D projection (#615). - Note:
project(_:)(thePoint2Doverload),Point2D.distance(to:)andnearestParameter(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,11away where the truth is7.81— ornilwhere 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
Curve2DProjectionvalues, 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:
Curve2DExtremaResultfor the closest pair of points, ornilon 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
Curve2DExtremaResultvalues (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
parametersis empty. - OCCT:
Geom2dGridEval_Curve::EvaluateGridviaOCCTCurve2DEvaluateGrid. - 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 ifparametersis empty. - OCCT:
Geom2dGridEval_Curve::EvaluateGridD1viaOCCTCurve2DEvaluateGridD1. - 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 ofsquareDistance.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:
circleRadiusmust 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:
circleRadiusmust 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 onc1;first2/last2— parameter range onc2. - Returns: Array of
Extrema2DResultvalues 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
CurInfPointwith.curvatureMinimumor.curvatureMaximumtype. - OCCT:
GeomLProp_CurAndInf2d::PerformCurExt, viacurvatureExtrema(). - 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, viainflectionPoints(). - 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
Intersection2DPointvalues. - OCCT:
IntAna2d_AnaIntersection(line–circle). - Note:
circleRadiusmust 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 aparam2of 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
Intersection2DPointvalues. - 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)wheredeviationis the actual maximum deviation, ornilif 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), ornilif 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:
trueif 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, ornilon 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 (default1e-6, matching the value this method used to hardcode). - Returns: Interpolated BSpline, or
nilon 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
nilon 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
nilon 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.0on failure (e.g. a non-finite bound). Arc length is otherwise always non-negative, so-1.0is unambiguous and never collides with a genuine zero-length result (e.g.u1 == u2). Uselength(from:to:)directly if you need an optional. - OCCT:
GCPnts_AbscissaPoint::Length(adaptor, u1, u2), subdivided perGeomAbs_CNinterval, the same measurement aslength(from:to:)(#603). - Note:
.nanand±.infinityreturn-1.0rather than propagating into the result. The pre-#548 unranged path could return+infinityfor 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.0and 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 into0...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::C0BSplineToArrayOfC1BSplineCurveand 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
}
isDone—trueif 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 onselfandother;seedU/seedV— initial parameter guesses onselfandother. - Returns:
LocalExtrema2dResult(checkisDonebefore 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), ornilifradius ≤ 0. - OCCT:
gce_MakeCirc2d(center + radius constructor). - Note: Geometrically identical to
circle(center:radius:), and enforces the same radius precondition.gce_MakeCirc2ditself acceptsRadius >= 0, so the bridge adds the zero check to keep the two factories in agreement — before #411 this page documented theradius ≤ 0rejection 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), ornilif 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), ornilif 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), ornilon 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, andminorRadiusno larger thanmajorRadius). - Returns:
Curve2D(ellipse), ornilif either radius is non-positive orminorRadiusexceedsmajorRadius. - 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_MakeElips2daccepted zero radii and, formajorRadius: 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), ornilif 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), orniliffocal ≤ 0. - OCCT:
gce_MakeParab2d. - Note: Places the same curve as
parabola(focus:direction:focalLength:)once that factory’sfocusis set tocenter + direction * focal, and since #487 enforces the same focal-length precondition. OCCT itself acceptsfocal == 0through both routes (gp_Parab2ddocuments 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
nilon 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
Curve2Dvalues, ornilif 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, ornilon internal failure. Checkcode == .okbefore 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;.notConvergedis 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, ornilon 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:
Point2Dat parametert, ornilon failure. - OCCT:
Geom2d_Curve::ValueviaPoint2Dbridge. - 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/p2—Point2Dendpoints. - Returns:
Curve2D(trimmed line segment), ornilon 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— thePoint2Dto project. - Returns:
(parameter, distance)tuple, ornilwhen there is no curve to answer about. - OCCT:
occtNearestPointOnCurve2dRange, shared withproject(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
parameterof0is an ordinary success — projecting a segment’s own start point onto it returns exactly that — sonilis the only failure signal. The underlying bridge function used to return0on 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 }