docs / Curves & surfaces

Curves & surfaces

NURBS curves and surfaces, plus the evaluators for measuring them before they become solids.

Curves and surfaces are measurable before they become solids. pointAt() reads a point off the curve; thicken() turns the surface into one.

const rail = nurbsCurve([
  [0, 0, 0],
  [30, 0, 20],
  [60, 20, 20],
  [80, 40, 0],
]);

const mid = rail.pointAt(0.5);
if (mid.length !== 3) {
  throw new Error('pointAt should return a 3D point');
}

const panel = nurbsSurface({
  controls: [
    [[0, 0, 0], [0, 30, 10], [0, 60, 0]],
    [[30, 0, 12], [30, 30, 26], [30, 60, 12]],
    [[60, 0, 0], [60, 30, 10], [60, 60, 0]],
  ],
  degree: { u: 2, v: 2 },
});

return panel.thicken(2).color('beam');
JavaScript is off — the listing above is the whole example.

Calls

CallWhat it does
nurbsCurve(controlPoints: Vec3[], opts?: { degree?: number; weights?: number[]; knots?: number[]; closed?: boolean }) => Curve3D3D parametric NURBS curve specified by an explicit Geom_BSplineCurve control net.
spline3d(points: Vec3[], opts?: { tension?: number; closed?: boolean }) => Curve3DCatmull-Rom-to-cubic-Bezier convenience that interpolates the supplied points through a cubic NURBS curve.
hermiteG2(a: { point: Vec3; tangent: Vec3; curvature?: Vec3 }, b: { point: Vec3; tangent: Vec3; curvature?: Vec3 }) => Curve3DQuintic Hermite Curve3D that interpolates the two endpoints with matching positions, first derivatives (tangents), and second derivatives (curvatures).
PathBuilder.hermiteG2(a: HermiteEndpoint2D, b: HermiteEndpoint2D) => PathBuilderNURBS Slice D — 2D quintic-Hermite transition between two endpoints, each with prescribed point + first derivative (tangent) + optional second derivative (curvature).
nurbsSurface({ controls, degree, weights?, knots?, periodic? }) => SurfaceBuild a NURBS surface from an explicit control net + degree.
surfaceFromCurves(sections: Sketch[]) => SurfaceSkin a NURBS surface through 2+ closed Sketch cross-sections in declaration order.
surfaceFromBoundary(curves: [Curve3D, Curve3D, Curve3D, Curve3D], opts?: { continuity?: "C0" | "C1" | "C2" | ("C0" | "C1" | "C2")[]; sampling?: number }) => SurfaceBuild the shipped filling surface: one NURBS face through 4 boundary curves.
sew(surfaces: Surface[], opts?: { tolerance?: number; requireClosed?: boolean }) => ShapeStitch N surfaces into a shell or closed solid via OCCT BRepBuilderAPI_Sewing.
Surface.thicken(t: Editable<number>) => ShapeOffset both sides of this surface by t mm and return the closed solid Shape.
Surface.toShape() => ShapeWrap this surface as a single-face zero-volume Shape (TopoDS_Shell).
Surface.trimTo(by: Surface) => SurfaceTrim this surface at its intersection with by (a Surface cutter) and return a new Surface representing the kept half.
Surface.split(by: Surface) => [Surface, Surface]Split this surface at its intersection with by (a Surface cutter) and return both resulting Surface halves as [first, second], ordered by descending face area.
Shape.projectCurve(opts: { source: ProjectCurveSource; face: FaceSelector | string; scaleMode?: 'original' | 'native' | 'bounds'; asEdge?: boolean }) => SketchWrap a 2D closed curve onto a 3D face along the face normal.
Curve3D.sample(n: number) => [number, number, number][]Sample n + 1 evenly-spaced points along the curve in the public [0, 1] parameter domain.
Curve3D.pointAt(t: number) => [number, number, number]World-space point on the curve at parameter t ∈ [0, 1] (clamped).
Curve3D.tangentAt(t: number) => [number, number, number]Unit tangent vector at parameter t ∈ [0, 1] (clamped).
Curve3D.domain() => [number, number]Parametric domain.
Curve3D.analytics.closestPoint(pt: Vec3, opts?: { tolerance?: number }) => Vec3World-space closest point on the curve to the query pt (Newton-Raphson).
Curve3D.analytics.closestParam(pt: Vec3, opts?: { tolerance?: number }) => numberParametric coordinate t ∈ [0, 1] of the closest point on the curve to pt.
Curve3D.analytics.divideByEqualArcLength(n: number) => CurveLengthSample[]Divide the curve into n equal-arc-length segments; returns n + 1 { t, pt, arcLength } samples covering both endpoints.
Curve3D.analytics.divideByArcLength(arcLength: number) => CurveLengthSample[]Sample the curve every arcLength mm starting from t = 0.
Curve3D.analytics.derivatives(t: number, numDerivs?: number) => Vec3[]Evaluate the curve and its first numDerivs derivatives at t ∈ [0, 1].
Curve3D.analytics.tessellate(opts?: { tolerance?: number }) => Vec3[]Adaptive polyline approximation of the curve at the given tolerance (mm).
Shape.intersect(...others) => ShapeBoolean intersection.
Curve3D.analytics.intersect(other: Curve3D | Surface, opts?: { tolerance?: number }) => CurveCurveIntersection[] | CurveSurfaceIntersection[]Geometric intersection of this curve with another Curve3D (returns { tA, tB, ptA, ptB, distance } records) or with a Surface from nurbsSurface() (returns { tCurve, uv, pt } records).