Skip to main content

Constraints

A sketch is two things: the geometry you draw and the relationships it has to satisfy. The geometry — every line, arc and circle — is a guess: a rough placement that gets the drawing started. Constraints state what must be true ("these ends touch", "this line is horizontal", "this arc is 20 wide"), and the solver moves the geometry until every statement holds. Change one number later and the whole sketch follows.

There are two kinds:

  • Positional constraints relate entities without a number: coincident, horizontal, tangent, symmetric, … They shape the drawing.
  • Dimensional constraints carry a value: distance, diameter, radius, angle. They size it.

In the viewport

  1. Open a sketch and click the entities to constrain — a line, an endpoint, an arc, a circle's center. Ctrl/Shift-click adds to the selection.
  2. The constraint toolbar appears under the sketch toolbar: Coincident, Horizontal, Vertical, Parallel, Perpendicular, Tangent, Equal, Concentric, Collinear, Midpoint, Symmetric, Fix, Dimension, Angle. Only the buttons that make sense for the selection are enabled; hovering a disabled one says what it needs ("pick two lines", "pick a point and a line, or three points").
  3. Hover a button to preview the solve as a ghost; click it to apply. The statement is written into the sketch body after the geometry, the solver re-solves, and a badge appears on the constrained entity — H for horizontal, V vertical, T tangent, parallel, perpendicular, = equal, concentric, collinear, M midpoint, S symmetric, F fix. Coincident points share one dot; a point held on a line or circle shows .
  4. Drag any geometry that is still free. The solver re-solves live, keeping every constraint.
The constraint bar: fourteen constraint buttons and a delete button, greyed out until a selection fits them
The constraint bar with nothing picked. Each button lists what it needs; every constraint page shows the bar with its button lit.
  1. 1
    Coincident
    two points, or a point and a line / arc / circle
  2. 2
    Horizontal
    one line, or two or more points
  3. 3
    Vertical
    one line, or two or more points
  4. 4
    Parallel
    two or more lines
  5. 5
    Perpendicular
    two lines
  6. 6
    Tangent
    a line and an arc / circle, or two arcs / circles
  7. 7
    Equal
    two or more lines, or two or more arcs / circles
  8. 8
    Concentric
    two arcs / circles
  9. 9
    Collinear
    two lines
  10. 10
    Midpoint
    a point and a line, or three points
  11. 11
    Symmetric
    two points and the mirror line
  12. 12
    Fix
    one point
  13. 13
    Dimension
    two points or entities, one line, or one circle / arc. Always enabled: with nothing picked it arms and waits for the picks
  14. 14
    Angle
    two lines
  15. 15
    Delete constraint
    a constraint badge picked in the viewport

Right-click a badge or a dimension label for its row menu; a constraint row in the timeline (the sketch's N constraints group) can be removed there.

Constraints while you draw

The sketch dialog has an Auto-constraints → Infer while drawing toggle (on by default). With it on, the drawing tools write constraints as you go: snapping a new endpoint onto an existing vertex emits coincident, and a line drawn close to an axis emits horizontal or vertical. Hold Ctrl while committing a point to skip the inference once. The Show constraints toggles — Dimensional and Positional — hide either family of badges when a dense sketch gets busy.

The canonical rectangle

Four sloppy lines become an exact 100 × 50 rectangle. Select two line ends and click Coincident for each corner; select a side and click Horizontal or Vertical; select the bottom-left end and click Fix; then Dimension the bottom and the right side.

Rectangle solved from rough guesses

The code behind it

Geometry first, then constraints — the layout the UI writes. Each coincident pins a corner, horizontal / vertical square the sides, fix anchors the profile on the plane, and the two distance dimensions set the size.

plate.part.js
import { sketch, line } from 'fluidcad/core';
import { coincident, horizontal, vertical, fix, distance } from "fluidcad/constraints";

sketch("xy", () => {
// The guesses are rough on purpose — the constraints do the work.
const b = line([1, -2], [99, 3]);
const r = line([99, 3], [101, 52]);
const t = line([101, 52], [-2, 48]);
const l = line([-2, 48], [1, -2]);
coincident(b.end(), r.start());
coincident(r.end(), t.start());
coincident(t.end(), l.start());
coincident(l.end(), b.start());
horizontal(b);
vertical(r);
horizontal(t);
vertical(l);
fix(b.start(), [0, 0]);
distance(b.start(), b.end(), 100);
distance(r.start(), r.end(), 50);
})

The result is fully constrained — zero degrees of freedom — and renders green.

The catalog

StatementMeaningRemoves
fix(p [, [x, y]])Pins a point, optionally at explicit coordinates2 DOF
coincident(a, b)Two points coincide, or a point lies on a line / arc / circle2 / 1
horizontal(l) · horizontal(p1, p2, …)The line is horizontal, or the points share a y1 per pair
vertical(l) · vertical(p1, p2, …)The line is vertical, or the points share an x1 per pair
parallel(a, b, …)Two or more lines are parallel1 per pair
perpendicular(a, b)Two lines meet at 90°1
symmetric(a, b, l)Two points mirror across a line or axis2
midpoint(p, l) · midpoint(p, a, b)A point at a line's midpoint, or halfway between two points2
tangent(a, b)A line and a circle/arc, or two circles/arcs, touch smoothly1
equal(a, b, …)Equal lengths (lines) or equal radii (arcs / circles)1 per pair
concentric(a, b)Two arcs / circles share a center2
collinear(a, b)A line lies on another line's infinite extension (or on an axis)2
distance(a, b, value [, axis])Distance between points, a point and a line, two lines, or to a circumference1
diameter(c, value)Diameter of a circle or arc1
radius(a, value)Radius of an arc or circle1
angle(a, b, degrees)Counter-clockwise angle from one line to another1

All sixteen come from one module:

import { coincident, horizontal, vertical, parallel, perpendicular,
tangent, equal, concentric, collinear, midpoint, symmetric,
fix, distance, angle, radius, diameter } from 'fluidcad/constraints';

A statement targets geometry either as a whole (horizontal(l)) or through a point accessor: l.start(), l.end(), l.mid() (coincident only), a.center(), c.center(), or a point() statement itself.

Sketch datums

Every sketch carries three fixed entities that cost nothing and never move: origin(), xAxis() and yAxis(), importable from fluidcad/core. In the viewport they are the origin dot and the two axis lines; click them like any entity. Constrain against them instead of fixing arbitrary points — a profile centred on the origin stays centred when its size changes.

Datum-anchored circles

The code behind it
hub.part.js
import { sketch, circle, origin, xAxis, yAxis } from 'fluidcad/core';
import { coincident, symmetric, diameter, distance, equal } from "fluidcad/constraints";

sketch("xy", () => {
const hub = circle([2, 3], 30);
const left = circle([-42, 2], 16);
const right = circle([38, -1], 16);
coincident(hub.center(), origin());
coincident(left.center(), xAxis());
symmetric(left.center(), right.center(), yAxis());
equal(left, right);
diameter(hub, 30);
diameter(left, 16);
distance(left.center(), right.center(), 80);
})

Common uses:

coincident(c.center(), origin()) // center on the sketch origin
coincident(p, xAxis()) // point on the X axis
collinear(xAxis(), l) // line along the X axis
symmetric(a, b, yAxis()) // mirror pair across the Y axis
distance(origin(), c.center(), 40) // dimension from the origin
angle(xAxis(), ramp, 30) // angle against the horizontal

A constraint whose every target is fixed (two datums, say) has nothing to solve and is refused.

The two axis datums are also the directions the in-sketch transforms take — mirror(yAxis(), …), copy("linear", xAxis(), …) — where a bare "x" would mean the world axis. See Mirror.

Fixed references

project() brings existing 3D edges and faces into the sketch as fixed reference geometry: the solver never moves it, it renders locked, and your geometry constrains against it — tangent(bore, l), concentric(c, rim), coincident(p, outline.ref(2).start()). That is how a feature follows the shape it sits on.

Reading the solver's verdict

The viewport tells you the state of the solve at all times:

  • Green geometry is fully constrained — the solver has locked it.
  • Default-coloured geometry still has freedom. It rests at the guess and can be dragged; the solver re-solves live, honouring every constraint.
  • Red geometry is in conflict: the constraints cannot all hold. The status readout names the conflicting statements by file and line — remove or change one.
  • Redundant constraints (satisfied, but adding nothing) are named too. A tidy sketch keeps zero.

The status readout also counts the remaining degrees of freedom; "Fully constrained" means zero. See Distance → Degrees of freedom for how the count works.

A habit that keeps sketches predictable: constrain the shape first (coincident, horizontal, tangent, …), then the size (dimensions), then the position (fix or a datum), and watch the DOF counter fall to zero.

Multiple solutions: the guess picks the branch

Many constraint systems have more than one valid answer — a circle tangent to a line can sit on either side of it. The solver converges to the solution nearest the guesses, so the guess coordinates choose the branch. Draw roughly on the side you mean; exactness does not matter, the side does. The Tangent page shows both branches from identical constraints.