Loft
loft() creates a smooth transition between two or more sketch profiles on different planes. It's useful for shapes that change cross-section along their length.
import { sketch, loft, plane } from 'fluidcad/core';
import { circle, rect } from 'fluidcad/core';
const s1 = sketch("xy", () => {
circle(100)
})
const s2 = sketch(plane("xy", { offset: 100 }), () => {
rect(80).centered()
})
loft(s1, s2)

Multiple profiles
You can loft through more than two profiles:
import { sketch, loft, plane } from 'fluidcad/core';
import { circle, rect } from 'fluidcad/core';
const s1 = sketch("xy", () => { circle(100) })
const s2 = sketch(plane("xy", { offset: 50 }), () => { rect(60).centered() })
const s3 = sketch(plane("xy", { offset: 100 }), () => { circle(40) })
loft(s1, s2, s3)

The profiles must be on different planes (typically parallel planes at different offsets).
Thin loft
.thin() offsets the profile edges on each section to produce a thin-walled shell instead of a filled loft:
import { sketch, loft, plane } from 'fluidcad/core';
import { circle, rect } from 'fluidcad/core';
const s1 = sketch("xy", () => {
circle(100)
})
const s2 = sketch(plane("xy", { offset: 100 }), () => {
rect(80).centered()
})
loft(s1, s2).thin(5)

loft(s1, s2).thin(5) // 5 units outward
loft(s1, s2).thin(-5) // 5 units inward
loft(s1, s2).thin(5, -3) // dual offset
All profiles must be sketches with matching topology (same number of wires).
Accessing geometry
const l = loft(s1, s2)
l.endFaces() // face at the last profile
l.startFaces() // face at the first profile
l.sideFaces() // the transition surface(s)
l.endEdges() // edges at the last profile
l.startEdges() // edges at the first profile
l.sideEdges() // edges along the sides
l.internalFaces() // inner wall face(s) of a thin loft (closed profiles)
l.capFaces() // cap face(s) of a thin loft (open profiles)
Fusion scope
loft(s1, s2).new() // create a separate solid
loft(s1, s2).add() // fuse with touching solids (default)
loft(s1, s2).remove() // subtract from all intersecting solids
loft(s1, s2).remove().scope(box) // subtract only from the box