A world never sends a finished shape as a list of points. It sends how the shape is made: an outline turned on a lathe, a box with its edges rounded, a horn swept along a curve and ribbed. This page is the sculptor's kit: every tool a shape may use, in four groups, with what each makes or does, in plain words, and a short example for each group. How a shape is written (a shape is a function) is on Construction; each tool's exact call, with its parameters, units and defaults, is in the Appendix: shape tools.

Words used on this page:

Shape
A value of type shape: a surface of faces (mostly four-cornered) joined edge to edge, with named values on its points, edges and faces. Every tool here takes shapes and returns a new shape.
Outline
A flat line of points in (x, y), open or closed: the cross-section a solid is made from. Its type is outline.
Spine
A line of points in space, open or closed, that an outline is carried along. Its type is spine. It is called a spine, not a path, so that it is never confused with the path of a part (a.b).
Frame
A place on a spine and three directions there: the way the spine runs, and the two directions across it in which an outline is laid.
Channel
A named value on every point (or edge, or face) of a shape, left by one tool for a later one to read: how far along a lathe the point was, how sharp an edge is.
Selection
A way of naming some of a shape's faces, edges or points without numbering them: those facing up, those inside a box, the sharp edges.
Catmull-Clark
The usual smoothing: each level splits every four-cornered face into four and moves each point toward a weighted middle of its neighbors, so a coarse cage of faces becomes a smooth surface.

How a world uses them

A shape is a function whose result is a shape: fn name(param: type, ...) shape { block }. Inside its block it calls these tools; the block's last line is the shape. Several pieces are joined into one by join([...]) (Blocks: joining shapes). A part draws one shape in one material: part glass: panes(size), material: materials.lanternGlass. Tools are pure: each makes a new shape from its inputs and changes nothing, and the same inputs give the same shape on every machine.

1. Outlines and spines

Most solids start as a line: a flat outline to turn or sweep, or a spine in space to sweep it along. These tools make those lines, and a spine answers questions about itself.

ToolWhat it makes
outline.pointsan outline through the points you list
outline.polygon, outline.circlea regular polygon; a circle (a polygon of many sides)
outline.rectangle, outline.roundedRectanglea rectangle, with sharp or round corners
outline.ellipse, outline.arc, outline.staran ellipse; part of a circle; a star of so many tips
outline.beziera smooth curve from three or four control points
outline.hullthe tightest convex outline round some points, as a band stretched round pins
outline.offsetan outline grown outward or shrunk inward by a distance
outline.roughenan outline made rough like a coastline, from a seed
spine.points, spine.linea spine through points; a straight line
spine.circle, spine.arc, spine.helixa circle flat on the ground; part of one; a spiral climbing round the y axis
spine.througha smooth curve through every point you list
spine.bezier, spine.beziersone smooth curve from control points; several end to end

What a spine answers. s.length() is its length. s.frameAt(distance) is the frame that far along. s.spotAt(distance, across: meters) is the point (x, y, z) that far along, moved sideways: to the right looking along the spine, to the left when across is negative. s.fromEnd(distance) is the distance along of the place that far back from the end, so a place counted back from the end needs no arithmetic with the length. s.points() are the spine's points as it was built, one for each piece. s.nearest(p, within: d) is the distance from a place to the spine and the distance along at the nearest point, or None if the spine is farther than d; s.indexAt(along) and s.alongAt(index) turn a distance along into the number of the control point there (with a fraction for the way to the next) and back. s.headingAt(along) is the direction the spine runs there, and s.pointNamed(name) the distance along at a point given a name when the spine was built. Distances below 0 or past the end are accepted: the ends carry on straight. A feature the land holds, such as the walkway (Terrain), is a spine laid on the ground and answers the same questions; its spots are on the ground. The full table is Shape tools: what a spine answers. A closed outline also answers o.contains(p), whether a place lies inside it, which is how a flat area of the land is stated.

let stake = [(0, -0.22), (0.008, -0.20), (0.018, -0.12), (0.018, 0.244), (0, 0.244)];   // (out from the axis, up)
let lid   = outline.roundedRectangle(width: 0.4, height: 0.25, radius: 0.03);
let rail  = spine.through(points: [(0, 1, 0), (2, 1.2, -1), (4, 1, 0)], segments: 12);
let spot  = walkway.spotAt(walkway.fromEnd(1.8 m), across: -1.5 m);    // 1.8 m back from the end, 1.5 m to its left

A list of flat points, like stake, can be given wherever an outline is wanted: it is an open outline through them.

2. Making solids

A solid is made either whole, from a few numbers (a primitive), or from an outline (a generator).

ToolWhat it makes
boxa box, from its three sizes or from a range on each axis
cylinder, conea round column; a cone, or a cone with its tip cut off, each standing on the ground plane
sphere, torusa ball; a ring like a doughnut
plane, disca flat rectangular sheet facing up, cut into a grid; a flat round one
faceone flat polygon through points you give, seen from one side
bara straight bar of rectangular section from one point to another, of a given width and thickness
lathean outline turned round an axis, as wood is turned: posts, vases, bulbs, a balloon's envelope. It is a sweep of the outline round a circle.
extrudean outline pushed straight out to a depth: a plank, a letter, a wall with a window's outline
sweepan outline carried along a spine, perhaps twisting and growing or shrinking: a rope, a rail, a horn
sweepScaledthe same, the outline's size any function of the way along
lofta skin stretched through several different outlines, each placed in a frame you give: a boat's hull through its sections
loftAlongthe same, the outlines placed at stations along a spine

sphere, box and cylinder also describe a space rather than a surface: written the same way, they give an area its volume, area(self, sphere(radius: 4 m, at: (0, 0.2, 0))).

A worked example: the lantern's roof by loftAlong

The lantern's roof is hipped: its eaves are 19 cm square, it rises shallowly for a little way and then more steeply to a top 10.4 cm square, all within 5 cm of height, and a top block 10 cm square stands on it. Three square outlines at three heights describe it, and loftAlong skins them.

let hipped = loftAlong(
  profiles: [outline.rectangle(width: 0.19,  height: 0.19),        // the eaves
             outline.rectangle(width: 0.14,  height: 0.14),        // the change of pitch
             outline.rectangle(width: 0.104, height: 0.104)],      // under the top block
  spine: spine.line(from: (0, 0.388, 0), to: (0, 0.438, 0)),        // 5 cm straight up
  stations: [0, 0.28, 1],
  caps: false);                                                     // open at both ends

3. Combining and placing

These put shapes where they belong, copy them, and combine them.

ToolWhat it does
move, turn, scalemoves a shape by an offset; turns it about an axis; scales it, evenly or by a different factor along x, y and z
joinseveral shapes as one: join([a, b, c]) joins the shapes in its list, which a loop can build with List[shape]() <- ..., each ... yield ...
onBonea shape tied wholly to one bone of a body; joined with others tied to other bones it makes one skinned part (Fitted bodies)
skinChaina shape skinned along a chain of bones with the weights blended across each joint (to be built)
inFramea shape written upright about the origin, placed in a frame got from a spine: petals and leaves carried into a stalk's frames (to be built)
cuta shape cut by a plane, keeping one side and leaving the cut open: a dome open underneath
mirrora shape and its reflection in a plane, the seam stitched (points closer than merge welded; a negative merge stitches nothing)
symmetrizeone half of a shape kept and the other half replaced by its reflection
arrayLinear, arrayRadial, arrayAlongSpinecopies in a row; copies round an axis (a tower's battlements, a flower's petals); copies spaced along a spine, each following its turns
union, difference, intersectiontwo solids as one; one cut out of the other; only what both hold. Each may crease the line where the two surfaces meet.
weldpoints closer than a distance merged
bridgetwo shapes with an open edge each, joined by a band of faces between those edges
let side = join([                                          // one side of the lantern's tracery, on the front
  box(x: -0.052 ..= 0.052, y: 0.292 ..= 0.300, z: 0.056 ..= 0.062),       // the low rail
  bar(from: (-0.046, 0.300, 0.059), to: (-0.046, 0.326, 0.059), width: 0.006, thickness: 0.006)
]);
let fourWays = List[shape]() <- each f in 0 ..< 4 yield turn(side, by: f * 90 deg, about: up);
join(fourWays)                                             // the same side, four ways round

4. Changing a solid

These change a solid already made: its faces, its detail, its form, its surface.

Selections

Many of these tools change only part of a shape, so they ask which faces, edges or points. A selection says which, without numbering them:

SelectionWhat it chooses
alleverything
facing(up, within: 20 deg)the faces that face a direction, give or take an angle
inBox(y: 1 ..= 2)what lies inside a box; an axis left out is unbounded
sharperThan(30 deg)the edges where the faces meet at more than an angle
byChannel("tapes")whatever a channel marks above 0
boundarythe open edges, with a face on one side only
where(test)the points for which a function of the point is true: its position, its direction, its channels

Soft selections that fade at their edges (regions) are proposed on Recipes.

The tools

ToolWhat it does
Editing faces
extrudeFaceschosen faces pushed out, with walls back to where they were: a chimney from a roof, a step from a floor
insetchosen faces shrunk inside a border, and perhaps pushed in or out: a window's frame and its recess
solidifya thin sheet given thickness: a leaf, a cloth, a bowl's wall
bevel, bevelCornerschosen edges rounded or cut at an angle; chosen corners cut off
Refining and reducing
subdividea coarse cage smoothed into a fine surface (Catmull-Clark), creased edges kept sharp
decimatefewer triangles, losing as little of the form as it can
dissolvePlanarfaces that lie nearly flat together merged into one
voxelRemesha closed shape rebuilt as an even skin of faces, overlaps and all fused
multiresa coarse base and stored detail at finer levels: a surface sculpted in an authoring tool
Deforming
bend, twist, taperthe whole shape curled, wrung, or narrowed toward one end; twist and taper may follow any function of the way along
deform, deformAlongevery point moved by a function of where it is; or by a function of how far along the shape it is
curveDeformthe shape laid along a spine, following its bends
latticea cage of control points round the shape; move some and the shape follows smoothly
smoothbumps and corners eased, with or without shrinking
shrinkwrapthe shape pressed onto another's surface
Detailing
displacepoints pushed out or in along the surface's direction, by a channel or by a function of the point
ribs, marginRinga channel of rib heights over the generator's along and around: regular ribs; ribs grown as a shell grows them
roughenthe surface made uneven by seeded noise
crease, creaseAnglechosen edges kept sharp through smoothing; how sharp an edge must be to show as an edge when drawn
uvPlanar, uvBox, uvCylindrical, uvSphericalnew texture coordinates, projected flat from one side, from the three axes, round a cylinder, round a ball
uvAtnew texture coordinates worked out from each point's place by a function you write
fn tableTop() shape {                                      // a slab with its edges rounded
  let slab = box(x: -0.6 ..= 0.6, y: 0.72 ..= 0.75, z: -0.4 ..= 0.4);
  bevel(slab, edges: sharperThan(30 deg), width: 4 mm, segments: 3)
}

fn horn(seed: count) shape {                               // a spiral horn with ribs
  let swept = sweepScaled(profile: outline.ellipse(width: 2.5, height: 2, segments: 64),
                          spine: spine.arc(radius: 3.3, from: 0 deg, to: 0.9 rad, segments: 360),
                          scale: fn(u: fraction) number { 0.36 * (0.03 + 0.97 * (1 - u)) });
  let ribbed = ribs(swept, into: "ribs", frequency: 22, seed: seed);
  displace(ribbed, by: fn(p: surfacePoint) meters { p.ribs * 0.14 * 0.36 * (0.03 + 0.97 * (1 - p.along)) })
}

The horn's outline is an ellipse 2.5 by 2, scaled down to 0.36 of that at its root and to almost nothing at its tip; the spine is an arc 3 m long bending through 0.9 rad. ribs leaves a channel ribs of rib heights from 0 to 1; displace reads it, and the sweep's own along, to push each rib out by 0.14 of the horn's radius there.

Channels

A channel is a named value on every point, edge or face of a shape, left by one tool for a later one. This is how the steps of a shape talk to each other, and content may use them by name.

ChannelOnLeft byRead by
along, aroundpointsevery generator: along how far the outline had traveled, around where on the outline the point came from, each 0 to 1. A lathe is a sweep of its outline round a circle, so for a lathe along runs round the turn (0 at the start, 1 back where it began) and around runs along the outline (from its first point to its last: up a post, from the mouth to the crown of a balloon), the same meanings as a sweep'sribs, marginRing; content, as p.along
uvface cornersevery generator; primitives (each its natural ones); the uv projections and uvAt; mirrormaterials; content, as p.uv
creaseedgescrease; booleans with a creasesubdivide, smooth, decimate
a name of the content'spointsribs, marginRing (into: "ribs")displace(channel: ...); byChannel; content, as p.ribs

Content reads a channel by name, in a function a tool runs for each point (p.along, p.ribs), and selects by one (byChannel("ribs")). Content writes its own channels through the tools that write them (ribs, marginRing, crease). Every tool carries every channel through, so after a bend or a subdivision a point still knows where it was in its generator.

What a drawn shape gets

Other modellers

Some forms are made better by a modeller of their own than by sculpting. Each makes a finished form a part draws directly; it is not a shape the tools above change.

PackageWhat it makesSee
sdf and sdfmeshforms described by distance from a surface, combined by soft blends and cuts, then turned into a surface: blobs, fused organic formsRecipes
noiserocka rock of a given size, facets and bumpiness, from a seedDesigns
weberpenna tree of a species, grown from a seed, with its leaves or needlesGenerators
leafrosettea clump of leaves from a center: a hosta, a fern, a cluster of flowersGenerators
terraingen, terrainlodland: heights shaped and eroded, and ground drawn finer near the eyeTerrain
humanbodya person's body from measurementsA villager
garmentdrapecloth cut to a pattern and draped on a bodyFitted and grown
strandflowhair and fur, grown from a surfaceFitted and grown
groundcovergrass, flowers, moss and litter laid over the land by rulesGenerators

noiserock: the rock recipe. A rock is a pure function of its record and seed. The record's fields and defaults: seed (1), width (0.5), height (0.3), depth (0.4) (half extents, in meters), rings (10), slices (16), facets (7), facetDepth (0.78), bumpAmp (0.16), bumpFreq (1.4), roughAmp (0.025), flatBase (0.55), smooth (false) and tileMeters (2.1). The island's rocks come in three kinds and the edging stones in their own recipe:

RecipeFields it sets
squat rockseed: 500 + 17 * v, width: 0.5, height: 0.3, depth: 0.42, facets: 10, facetDepth: 0.7, bumpAmp: 0.12, rings: 9, slices: 14
tall rockthe same with width: 0.34, height: 0.55, depth: 0.32, facets: 8, facetDepth: 0.66, bumpAmp: 0.1
long rockthe same with width: 0.62, height: 0.32, depth: 0.3, facets: 9, facetDepth: 0.68, bumpAmp: 0.16
edging stoneseed: 800 + 13 * v, width: 0.12 + 0.025 * (v % 2), height: 0.075, depth: 0.1 + 0.03 * (v / 2) (whole division), facets: 4, facetDepth: 0.9, bumpAmp: 0.12, flatBase: 0.45, rings: 6, slices: 10, smooth: true, tileMeters: 1.5

Variant v picks the kind by v % 3 for rocks. The rock's granite paint is surfacepatterns' RockParams.granite(size, seed) (Textures).

leafrosette: three clumps. Each is a record with a seed (1), made into a mesh standing on y = 0:

CallFields (defaults)The island's
hostaMesh(HostaParams)leaves (9), length (0.30), width (0.62), across (3), rows (4), droop (70)seed: 40 + 7 * v
fernMesh(FernParams)fronds (9), length (0.7), pairs (12), leaflet (0.17), arch (95)seed: 60 + 11 * v
flowerClumpMesh(FlowerParams)flowers (5), height (0.30), spread (0.12), petals (8), head (0.025)seed: 80 + 5 * v, flowers: 4 + 3 * (v % 2), height: 0.38, head: 0.036, spread: 0.16

sculptbrush (brush strokes), quadremesh (rebuilding a surface as tidy four-cornered faces) and shapefit (measuring and fitting) are tools for making worlds, used by authoring programs; they are not content vocabulary.

What it means

Limits of the tools today

How it connects

Open questions

  1. How a sculpted surface travels. Strokes, a coarse base with stored detail (multires), or not as content at all.
  2. Method or shape: a swept horn or a distance-field horn; does content name the tool or describe the shape?
  3. Versioning: a shape's meaning depends on its tools' versions; how a tool's version is stated and kept is not designed.

← Composing the tools | Contents | Generators and scattering →