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
- Lofting & Sweeps — single-family skinning.
- Healing & Validity — clean up a sewn surface model.
- API mapping:
../../API_REFERENCE.md