Lofts, Sweeps, Revolves
Three operations turn a 2D profile (or several) into a 3D solid by motion: extrude moves the profile linearly, revolve rotates it around an axis, sweep drags it along a path, loft interpolates between profiles. Combined with sketching, they cover everything that isn't a primitive boolean.
Extrude — linear motion
The simplest case. A profile pushed along its normal:
import { sketchRectangle, sketchCircle, measureVolume, unwrap } from 'brepjs/quick';
const block = sketchRectangle(30, 20).extrude(10);
const cyl = sketchCircle(10).extrude(20);
console.log('Block:', unwrap(measureVolume(block)).toFixed(2));
console.log('Cylinder:', unwrap(measureVolume(cyl)).toFixed(2));The functional equivalent — extrude(face, height) — takes an OrientedFace and a distance:
import { sketchCircle, extrude, unwrap } from 'brepjs/quick';
const profile = sketchCircle(10).face();
const cyl = unwrap(extrude(profile, 20));
export default cyl;Extrude with a vector
Pass a 3D vector to extrude in an arbitrary direction (not perpendicular to the sketch plane):
import { sketchRectangle, extrudeAlong, unwrap } from 'brepjs/quick';
const profile = sketchRectangle(20, 10).face();
const slanted = unwrap(extrudeAlong(profile, [0, 5, 20])); // extrude along (0,5,20)
export default slanted;The vector replaces the height — its length determines extrusion distance, its direction determines the axis.
Tapered extrude
extrude(face, height, { taper }) adds a draft angle. Common in moulded parts:
import { sketchRectangle, extrude, unwrap } from 'brepjs/quick';
const profile = sketchRectangle(30, 20).face();
const tapered = unwrap(extrude(profile, 20, { taper: 5 })); // 5° draft
export default tapered;Negative taper widens upward; positive narrows.
Revolve — rotational motion
A 2D profile rotated around an axis — the basis of every wineglass, vase, axle, and spindle:
import { Sketcher } from 'brepjs/quick';
const goblet = new Sketcher('XZ')
.movePointerTo([0, 0])
.hLine(8) // base outer radius
.vLine(1.5) // thin disc base
.hLine(-6) // narrow into the stem (r=2)
.vLine(20) // stem
.hLine(8) // cup floor at r=10
.tangentArc(4, 4) // quarter-arc rounding the cup base, ends tangent +Z
.vLine(13) // straight cup wall up to r=14
.hLine(-14) // close across the top
.vLine(-38.5) // close down the axis
.close()
.revolve();
export default goblet;The axis defaults to the sketch plane's vertical axis. The profile must lie entirely on one side of the axis — the kernel rejects profiles that cross.
Partial revolves
By default, revolve sweeps 360°. For partial revolutions:
import { Sketcher, revolve, unwrap } from 'brepjs/quick';
const profile = new Sketcher('XZ')
.movePointerTo([5, 0])
.lineTo([10, 0])
.lineTo([10, 5])
.lineTo([5, 5])
.close()
.face();
const halfRing = unwrap(revolve(profile, { angle: 180 })); // half-torus segment
export default halfRing;{ angle: degrees } controls the sweep. Use this for hemisphere caps, pie slices, partial bushings.
Revolves around an arbitrary axis
import { Sketcher, revolve, unwrap } from 'brepjs/quick';
const profile = new Sketcher('XY')
.movePointerTo([20, 0])
.lineTo([22, 0])
.lineTo([22, 5])
.lineTo([20, 5])
.close()
.face();
const ring = unwrap(revolve(profile, { axis: { origin: [0, 0, 0], direction: [0, 0, 1] } }));
export default ring;Sweep — profile along a path
Drag a 2D profile along a 3D wire:
import { sketchCircle, line, wire, sweep, unwrap } from 'brepjs/quick';
const cross = sketchCircle(2).wire; // 2 mm tube cross-section (closed wire)
const path = unwrap(wire([line([0, 0, 0], [0, 0, 50])])); // 50 mm straight up
const tube = unwrap(sweep(cross, path));
export default tube;sweep takes a closed wire (the profile) and a wire (the spine). line(p1, p2) returns an Edge, so we wrap it with wire([...]) to promote it to a Wire. The path can be any 3D wire — straight, arced, helical, B-spline. The profile rides along it.
Helical sweeps for threads and springs
Build a helix as the path, sweep a circle along it:
import { sketchCircle, helix, sweep, unwrap } from 'brepjs/quick';
const profile = sketchCircle(0.5).wire; // thread cross-section (closed wire)
const path = helix(1.5, 30, 5); // pitch, height, radius
const thread = unwrap(sweep(profile, path)); // bare helical coil
export default thread;helix(pitch, height, radius) returns a wire of the helical curve. Sweep a small circle along it and you get a thread — fuse it onto a shaft to make a screw (see Threaded fastener below).
Frenet vs auxiliary frame
When the path bends, the kernel has to decide how the profile orients itself. Two modes:
'frenet'(default) — the profile rotates with the path's tangent and normal'auxiliary'— the profile's normal stays aligned with a fixed reference axis
Most parts work with the default. Specify { frame: 'auxiliary' } when you need the profile to keep a constant up-direction (think extruded handrails).
Loft — interpolation between profiles
A solid built by smoothly interpolating between two or more profiles:
import { sketchCircle, loft, unwrap } from 'brepjs/quick';
const base = sketchCircle(15); // narrow base
const rim = sketchCircle(35, { origin: [0, 0, 50] }); // wider rim, 50 mm up
const bowl = unwrap(loft([base.wire, rim.wire])); // flared truncated cone
export default bowl;The loft passes through every input profile in order. Two parallel circles of different radii produce a flared, bowl-shaped solid — the canonical loft pattern. Hollow it with shell (see Hollowing: shell below) to turn it into a usable cup. Profiles must be on parallel planes (or roughly so — non-coplanar lofting works but can produce twisted results).
Note:
lofttakesWire[], hence the.wireon each sketch. The OO equivalentsketch.loftWith(otherSketch, opts)does that unwrapping for you.
Multi-section lofts
import { sketchCircle, loft, unwrap } from 'brepjs/quick';
const sections = [
sketchCircle(10).wire,
sketchCircle(20, { origin: [0, 0, 30] }).wire,
sketchCircle(5, { origin: [0, 0, 60] }).wire,
];
const vase = unwrap(loft(sections));
export default vase;Three or more sections produces a smooth blend through every one. Useful for vases, ducts, ergonomic handles.
Ruled vs smooth
loft([a, b], { ruled: true }) builds a ruled surface (straight lines between corresponding points on the profiles). The default produces a smooth (B-spline) blend. Ruled is faster but visually angular — common in sheet-metal modelling.
Hollowing: shell
After building a solid, hollow it out by removing one or more faces and offsetting the rest by a wall thickness:
import { sketchCircle, faceFinder, shell, unwrap } from 'brepjs/quick';
const closed = sketchCircle(20).extrude(40);
// Top face is the only one whose minimum distance from the origin is 40 (the cylinder's height).
const topFaces = faceFinder().atDistance(40, [0, 0, 0]).findAll(closed);
const cup = unwrap(shell(closed, topFaces, 2)); // 2mm wall, top open
export default cup;shell(solid, openFaces, thickness). The top face becomes the open mouth; everything else gains the wall thickness inward.
The fluent equivalent:
import { shape, sketchCircle } from 'brepjs/quick';
const cup = shape(sketchCircle(20).extrude(40)).shell(
(f) => f.atDistance(40, [0, 0, 0]),
2
).val;
export default cup;Common patterns
Threaded fastener
import { sketchCircle, helix, sweep, sketchRoundedRectangle, fuse, translate, unwrap } from 'brepjs/quick';
const shaft = sketchCircle(3).extrude(20); // M6-ish, 20 mm long
const threadProfile = sketchCircle(0.75).wire; // ~12% of shaft dia
const threadPath = helix(1.5, 18, 3); // pitch, height, radius
const thread = unwrap(sweep(threadProfile, threadPath));
const head = translate(sketchRoundedRectangle(8, 8, 0.5).extrude(3), [0, 0, 20]);
const screw = unwrap(fuse(unwrap(fuse(shaft, thread)), head));
export default screw;Funnel (loft + revolve combined)
A funnel is two truncated cones — but easier as a revolved profile:
import { Sketcher } from 'brepjs/quick';
const funnel = new Sketcher('XZ')
.movePointerTo([15, 0])
.lineTo([15, 1])
.lineTo([3, 30])
.lineTo([3, 50])
.lineTo([2, 50])
.lineTo([2, 30])
.lineTo([14, 1])
.lineTo([14, 0])
.close()
.revolve();
export default funnel;A profile, two lineTos, a revolve. Simpler than building two cones and fusing.
When operations fail
| Failure | Cause |
|---|---|
EXTRUDE_INVALID_FACE | The face isn't OrientedFace — usually means the wire isn't closed |
REVOLVE_AXIS_CROSSES_PROFILE | The axis passes through the profile (would self-intersect) |
SWEEP_PATH_INVALID | Path has self-intersections, sharp corners, or non-tangent meeting edges |
LOFT_PROFILE_MISMATCH | Profiles have very different topologies (e.g. one closed, one open) |
SHELL_TOO_THICK | Wall thickness larger than the local geometry can support |
Error Codes covers each in detail.
Next steps
- Boolean Operations — combining the solids you build here
- Fillets & Chamfers — refining the edges these operations create
- Finders & Queries — selecting features to extrude / shell / fillet on