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 } from 'brepjs/quick';
const block = sketchRectangle(30, 20).extrude(10);
const cyl = sketchCircle(10).extrude(20);
console.log('Block:', measureVolume(block).toFixed(2));
console.log('Cylinder:', 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));
void 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)
void 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
void 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)
.vLine(2)
.hLine(-6)
.vLine(20)
.hLine(15)
.tangentArc(0, 4)
.hLine(-17)
.vLine(-26)
.close()
.revolve();
void 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
void 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] } }));
void ring;Sweep — profile along a path
Drag a 2D profile along a 3D wire:
import { sketchCircle, line, sweep, unwrap } from 'brepjs/quick';
const cross = sketchCircle(2).face(); // 2mm tube radius
const path = line([0, 0, 0], [0, 0, 50]); // 50mm straight up
const tube = unwrap(sweep(cross, path));
void tube;The path can be any 3D curve — 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 thread = sketchCircle(0.5).face(); // thread cross-section
const path = helix({ pitch: 1.5, height: 30, radius: 5 });
const screw = unwrap(sweep(thread, path));
void screw;helix({ pitch, height, radius }) returns a wire of the helical curve.
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, sketchRectangle, loft, unwrap } from 'brepjs/quick';
const bottom = sketchCircle(20); // round base
const top = sketchRectangle(30, 30).translate([0, 0, 50]); // square top
const bowl = unwrap(loft([bottom, top]));
void bowl;The loft passes through every input profile in order. Profiles must be on parallel planes (or roughly so — non-coplanar lofting works but can produce twisted results).
Multi-section lofts
import { sketchCircle, loft, unwrap } from 'brepjs/quick';
const sections = [
sketchCircle(10),
sketchCircle(20).translate([0, 0, 30]),
sketchCircle(5).translate([0, 0, 60]),
];
const vase = unwrap(loft(sections));
void 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);
const topFaces = faceFinder().inDirection('Z').withZ({ min: 39 }).findAll(closed);
const cup = unwrap(shell(closed, topFaces, 2)); // 2mm wall, top open
void 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.inDirection('Z').withZ({ min: 39 }),
2
).val;
void cup;Common patterns
Threaded fastener
import { sketchCircle, helix, sweep, sketchRoundedRectangle, fuse, unwrap } from 'brepjs/quick';
const shaft = sketchCircle(3).extrude(20);
const threadProfile = sketchCircle(0.5).face();
const threadPath = helix({ pitch: 1.5, height: 18, radius: 3 });
const thread = unwrap(sweep(threadProfile, threadPath));
const head = sketchRoundedRectangle(8, 8, 0.5).extrude(3).translate([0, 0, 20]);
const screw = unwrap(fuse(unwrap(fuse(shaft, thread)), head));
void 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();
void 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