Skip to main content

Rectangle

A rectangle in one gesture: plates, pockets, bosses and the outline most parts start from. The tool writes four lines and the constraints that keep them a rectangle, so every side and corner stays editable afterwards.

In the viewport

  1. Click Rectangle on the sketch toolbar. The button opens a small menu with two toggles: Rounded adds a corner-radius step to the gesture, Centered grows the rectangle from its middle instead of from a corner.
  2. Click the first corner (or the centre, with Centered on). The X / Y pill places it exactly.
  3. Move to the opposite corner and click — or type the W width, press Enter, type the H height and press Enter. Where the mouse sits relative to the first corner picks which way a typed size grows.
  4. With Rounded on, the pill switches to R: type the corner radius or click to set it.

Typed sizes become dimensions in the sketch; a size set by clicking stays a guess you can drag. A first corner that snaps onto a vertex or the origin gets a coincident written for it, and so does an opposite corner that snaps onto one (both under Auto-constraints; hold Ctrl to skip one).

An 80 × 50 rectangle with one corner on the origin

The code behind it
plate.part.js
import { sketch, line, origin } from 'fluidcad/core';
import { coincident, horizontal, vertical, distance } from 'fluidcad/constraints';

sketch("xy", () => {
// One Rectangle gesture: the first corner clicked on the origin, then
// 80 for the width and 50 for the height typed in the pill. The tool
// writes four lines, counter-clockwise from that corner.
const b = line([0, 0], [80, 0]);
const r = line([80, 0], [80, 50]);
const t = line([80, 50], [0, 50]);
const l = line([0, 50], [0, 0]);
// The corners: each side ends where the next one starts.
coincident(b.end(), r.start());
coincident(r.end(), t.start());
coincident(t.end(), l.start());
coincident(l.end(), b.start());
// The sides stay axis-aligned whatever else moves.
horizontal(b);
horizontal(t);
vertical(r);
vertical(l);
// Typed sizes become dimensions. A size set by clicking would have
// stayed a guess, free to drag.
distance(b.start(), b.end(), 80);
distance(r.start(), r.end(), 50);
// The first click snapped onto the origin, so that corner is pinned to it.
coincident(b.start(), origin());
})

What the tool writes is ordinary geometry: four lines, a coincident at each corner, horizontal on the top and bottom, vertical on the two sides, and a distance for each typed size. Nothing is special about the result — drag a side, dimension a corner to something else, or delete a constraint and the shape follows.

Centered

With Centered on, the first click is the centre. No vertex of the rectangle sits there, so a snapped centre cannot become a coincident. The tool writes a midpoint instead: the snapped point is held halfway between two diagonally opposite corners, which pins the centre just as firmly.

midpoint(origin(), b.start(), t.start()); // the centre stays on the origin

Rounded

With Rounded on, the gesture ends with a radius, and the outline arrives as four lines and four corner arcs: coincident and tangent at each of the eight junctions, horizontal / vertical on the sides, one radius dimension on the first arc with the other three equal to it. The typed width and height are written as distances between opposite sides, since the sharp corners no longer exist.

A 120 × 66 rounded rectangle, R13, centred on the origin

The code behind it
plate.part.js
import { sketch, line, arc, origin } from 'fluidcad/core';
import { coincident, tangent, horizontal, vertical, equal, distance, radius, midpoint } from 'fluidcad/constraints';

sketch("xy", () => {
// Rounded + Centered: the centre clicked on the origin, then 120 (W),
// 66 (H) and 13 (R) typed. The loop runs counter-clockwise from the
// bottom side: line, corner arc, line, corner arc, ...
const bottom = line([-47, -33], [47, -33]);
const br = arc([47, -33], [60, -20], [47, -20]);
const right = line([60, -20], [60, 20]);
const tr = arc([60, 20], [47, 33], [47, 20]);
const top = line([47, 33], [-47, 33]);
const tl = arc([-47, 33], [-60, 20], [-47, 20]);
const left = line([-60, 20], [-60, -20]);
const bl = arc([-60, -20], [-47, -33], [-47, -20]);
// Eight junctions, each closed and made smooth.
coincident(bottom.end(), br.start());
coincident(br.end(), right.start());
coincident(right.end(), tr.start());
coincident(tr.end(), top.start());
coincident(top.end(), tl.start());
coincident(tl.end(), left.start());
coincident(left.end(), bl.start());
coincident(bl.end(), bottom.start());
tangent(bottom, br);
tangent(br, right);
tangent(right, tr);
tangent(tr, top);
tangent(top, tl);
tangent(tl, left);
tangent(left, bl);
tangent(bl, bottom);
horizontal(bottom);
horizontal(top);
vertical(right);
vertical(left);
// One radius for all four corners: the first arc carries the
// dimension, the other three are equal to it.
equal(br, tr);
equal(br, tl);
equal(br, bl);
// Overall width and height are side-to-side distances, because the
// sharp corners no longer exist.
distance(left, right, 120);
distance(bottom, top, 66);
radius(br, 13);
// The centre has no vertex on it either, so the snapped origin becomes
// the midpoint between two diagonal corner-arc centres.
midpoint(origin(), bl.center(), tr.center());
})

A rounded rectangle is what the sketch Fillet tool would produce from a plain one — the same arcs and constraints, in one gesture.

By hand

Writing the four lines out yourself is fine — the first example above is the complete recipe: a coincident at each corner, horizontal on two opposite sides, vertical on the other two, two distance constraints for the size and one pinned corner. For hand-written models there is also a shorter form, rect() from fluidcad/shapes:

import { rect } from 'fluidcad/shapes';

rect([0, 0], 80, 50) // bottom-left corner, width, height
rect([0, 0], 30) // a square — the height defaults to the width
rect([40, 25], 80, 50).centered() // the position is the centre
rect([0, 0], 80, 50).radius(10) // rounded corners, one shared radius

A rect() is one statement and one timeline row. Its corner, horizontal and vertical rules are internal to the shape: there are no constraint statements to read, and none to delete by accident. What you constrain from outside are its edges and their points, through the accessors below.

const r = rect([0, 0], 80, 50);
coincident(r.bottom().start(), origin()); // pin the position corner
distance(r.left(), r.right(), 80); // width
distance(r.bottom(), r.top(), 50); // height — now fully constrained
Every argument is a guess

rect([0, 0], 80, 50) has four degrees of freedom: position, width and height are starting values, not dimensions, exactly like the coordinates of a line(). Pin the position with a coincident or fix on a corner point and size it with two distance constraints. .radius(10) adds a fifth degree of freedom rather than locking anything — put a radius() dimension on a corner arc to fix it.

Accessors

AccessorMeaning
r.bottom()The side leaving the position corner, then r.right(), r.top(), r.left() counter-clockwise
r.bottom().start() / .end()The endpoints of a side
r.corner(i)Corner arc i of a rounded rect: 0 at the position corner, counter-clockwise
r.corner(i).center()The centre of that corner arc

The sides accept the same constraints as any line — distance between two opposite sides sizes the shape, coincident(r.left().start(), origin()) places it. The corner arcs take radius, equal and tangent.

Modifiers

rect([0, 0], 80, 50).guide() // construction geometry — see Guides
rect([0, 0], 80, 50).name('plate') // name it for the timeline

.centered() and .radius() chain in any order, once each.