Link Search Menu Expand Document

Gordon Surfaces

A Gordon surface is built to pass through a network of curves — two families that cross to form a grid: profiles (running one way, the U direction) and guides (running across them, V). Where a loft interpolates a single family of sections and a Coons patch fills four boundary curves, a Gordon surface honours the whole interior network — every profile and every guide lies on the result. It’s the tool for skinning a hull, a turbine blade, or any panel defined by a wireframe of feature curves.

OCCTSwift wraps OCCT’s GeomFill_Gordon (and the lower-level GeomFill_NetworkSurface).

Build from a curve network

Give it ≥ 2 profiles and ≥ 2 guides. The catch is the grid must close: each profile must meet each guide, and shared corners must coincide (within tolerance). Here a domed 2×2 network — two profiles bowed up in X, two guides bowed up in Y, meeting at four coplanar corners:

// profiles (U): the y = 0 and y = 10 edges, bowed up to z = 3 mid-span
guard let p1 = Curve3D.interpolate(points: [SIMD3(0, 0, 0), SIMD3(5, 0, 3), SIMD3(10, 0, 0)]),
      let p2 = Curve3D.interpolate(points: [SIMD3(0, 10, 0), SIMD3(5, 10, 3), SIMD3(10, 10, 0)]),
      // guides (V): the x = 0 and x = 10 edges, bowed up to z = 2 mid-span
      let g1 = Curve3D.interpolate(points: [SIMD3(0, 0, 0), SIMD3(0, 5, 2), SIMD3(0, 10, 0)]),
      let g2 = Curve3D.interpolate(points: [SIMD3(10, 0, 0), SIMD3(10, 5, 2), SIMD3(10, 10, 0)])
else { return }

guard let surface = Surface.gordon(profiles: [p1, p2], guides: [g1, g2], tolerance: 1e-3) else { return }

The four corners are shared between a profile and a guide — p1 starts at (0,0,0) where g1 starts, and so on. The interior bows (z = 3 on the profiles, z = 2 on the guides) need not match; the Gordon construction blends the two families into one B-spline.

To render or sew the result, turn the Surface into a face:

let face = surface.toFace()        // -> Shape?  (a trimmed face over the surface's UV domain)

A Gordon surface through a 2×2 domed network

Diagnose a build with gordonReport

A network can fail in many specific ways (the curves don’t intersect, reparametrization fails, the result isn’t rational-compatible…). gordonReport returns the surface plus a status and an isApproximate flag instead of a bare nil:

let report = Surface.gordonReport(profiles: [p1, p2], guides: [g1, g2], tolerance: 1e-3)
switch report.status {
case .done:        print("exact:", report.surface != nil, "approx:", report.isApproximate)
case .invalidInput, .intersectionFailed, .compatibilityFailed:
    print("network problem:", report.status)
default:           print("build failed:", report.status)
}

By default the build is exact-only — it returns no surface if it can’t interpolate the network exactly. Set allowApproximateFallback: true to accept a sampled B-spline approximation when the exact construction fails (the result is then flagged isApproximate):

let r = Surface.gordonReport(profiles: [p1, p2], guides: [g1, g2],
                             allowApproximateFallback: true)
// r.surface may be non-nil with r.isApproximate == true

The lower-level network builder

networkSurface exposes OCCT’s raw GeomFill_NetworkSurface directly and returns its own status, rather than gordon’s full curve-reordering-and-reparametrization pipeline. It finds each profile/guide pair’s real contact point and locates it in the other family’s own parameter domain (not a caller-invented fraction), which is enough for it to report .done on the same 2×2 domed network above:

let (surface, status) = Surface.networkSurface(profiles: [p1, p2], guides: [g1, g2], tolerance: 1e-3)
if status != .done { print("network builder declined:", status) }

Known limitation (#748): on every network tried so far, including a plain bilinear rectangle with nothing to approximate, networkSurface’s result is correct at the two corners on one diagonal and wrong at the other two — gordon/gordonReport on the identical curves are not affected. Prefer gordon/gordonReport for anything where the built surface’s shape matters, not just whether a status came back .done.

It is still the lower-level tool. It does not reorder a scrambled network, does not run gordon’s non-linear reparametrization pass when curve families disagree on where their shared knots should land, and does not derive rational contact weights from the input curves (every contact point is weighted 1.0). A network gordon can complete after reordering or reparametrizing can still come back .knotAlignmentFailed, .skinningFailed, or similar from networkSurface. Reach for gordon / gordonReport first; drop to networkSurface only when you need the low-level builder’s exact behaviour, or already have a network in the shape it expects.

Gordon vs. loft vs. fill

Want Use
Skin through one family of section curves Shape.loft
Fill between 2 or 4 boundary curves Surface.bsplineFill
Interpolate a full grid of profile + guide curves Surface.gordon
Interpolate a cloud of scattered points Surface.plateThrough

See also