Planar
A planar mate keeps two faces in contact and lets the second part move freely across the first: slide in X, slide in Y, spin about Z. It is the joint of a block on a table, a tile on a floor, a workpiece on a machine bed.
| Free motion | slide in X and Y, rotation about Z |
|---|
In the viewport
- Click Planar on the assembly toolbar and pick the two face connectors — the surface first, then the part that rests on it.
- The part drops onto the surface. Drag it around: it stays flat.
- Offset takes a Z value only, to hold the part a fixed distance above the face. Flip turns the part over.
- Click Apply.

The code behind it
clamp.part.js
clamp.part.js
import { part, sketch, line, extrude, chamfer, color, select, connector } from 'fluidcad/core';
import { face } from 'fluidcad/filters';
import { coincident, distance, fix, horizontal, vertical } from 'fluidcad/constraints';
// A 30 × 30 × 15 mm clamp block with a chamfered top — the part that slides
// around on the base plate in the planar example.
export const clamp = part('Clamp block', () => {
sketch('xy', () => {
const b = line([-15, -15], [15, -15]);
const r = line([15, -15], [15, 15]);
const t = line([15, 15], [-15, 15]);
const l = line([-15, 15], [-15, -15]);
coincident(b.end(), r.start());
coincident(r.end(), t.start());
coincident(t.end(), l.start());
coincident(l.end(), b.start());
horizontal(b);
horizontal(t);
vertical(r);
vertical(l);
fix(b.start(), [-15, -15]);
distance(b.start(), b.end(), 30);
distance(r.start(), r.end(), 30);
});
const body = extrude(15);
chamfer(2, body.endEdges());
color('seagreen');
connector('bottom', select(face().planar().onPlane('xy', 0)));
});
work-table.assembly.js
import { assembly, insert, mate } from 'fluidcad/core';
import { plate } from './asm-plate.part.js';
import { clamp } from './asm-clamp.part.js';
export const workTable = assembly('work-table', () => {
const table = insert(plate).grounded();
const block = insert(clamp).translate(-15, 8, 40).rotate('z', 20);
// Planar keeps the two faces in contact and leaves three degrees of
// freedom: slide in X, slide in Y, spin about Z. The block keeps the
// position and spin its insert pose gave it; drag it in the viewport
// and it stays flat on the plate.
mate('planar', table.connectors.top, block.connectors.bottom);
});
Options
mate('planar', table.connectors.top, block.connectors.bottom)
.offset(0, 0, 1) // hover 1 above the face (Z only)
.flip()
The in-plane X and Y are the mate's free directions, so .offset() refuses to pin them; where the block starts on the plate is the instance's own .translate(), and dragging it writes that back. .rotate() sets the rest spin; .limits() is not available.
Contact, not fastening
A planar mate does not hold the part down — the solver keeps the faces coplanar, and the part slides where you drag it. To fix a part on a surface, use fastened; to let it slide in one direction only, a slider.