Copy
Copy duplicates sketch geometry along one or two directions or around a centre point. The copies take part in the same face-building step as the rest of the sketch, so a copied circle is a hole like the original.
In the viewport
- Click Copy on the sketch toolbar. The Copy dialog opens docked on the right with a Type dropdown, Linear or Circular, and a Geometry slot.
- Click the sketch edges to copy. Picks accumulate as chips; click a picked edge again, or a chip's ✕, to drop it.
- Fill in the direction or the centre and the counts — the fields differ per type, see below. The copies preview in blue as you go, and the statement about to be written shows under the dialog.
- Click Apply.
Linear
- Direction (under Direction 1): click the slot, then a sketched line in the viewport, or the sketch's own X or Y axis line. An axis pick shows as the chip Sketch X axis and is written as
xAxis()/yAxis(). - Total Count: the number of instances, the original included.
- Spacing: Offset is the distance between neighbours; Total is the whole span, distributed evenly. The mode is shared by both directions.
- + Add second direction adds a Direction 2 group with its own direction, count and spacing, which turns the row into a grid.
- Centered spreads the copies symmetrically about the source instead of running off to one side.
- Skip lists the instances to leave out, by index (
1, 3); in a grid, by cell ([1, 0], [2, 1]).

The code behind it
import { sketch, line, arc, copy, extrude } from 'fluidcad/core';
import { coincident, tangent, vertical, fix, distance, radius, equal, horizontal } from 'fluidcad/constraints';
// A vent plate: one slot drawn, then copied into a rack.
sketch("xy", () => {
// The plate outline.
const b = line([0, 0], [120, 0]);
const r = line([120, 0], [120, 60]);
const t = line([120, 60], [0, 60]);
const l = line([0, 60], [0, 0]);
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(), 120);
distance(r.start(), r.end(), 60);
// One vertical slot: two lines closed by two cap arcs.
const left = line([15, 15], [15, 45]);
const cap1 = arc([15, 45], [25, 45], [20, 45]).cw();
const right = line([25, 45], [25, 15]);
const cap2 = arc([25, 15], [15, 15], [20, 15]).cw();
coincident(left.end(), cap1.start());
coincident(cap1.end(), right.start());
coincident(right.end(), cap2.start());
coincident(cap2.end(), left.start());
tangent(left, cap1);
tangent(cap1, right);
tangent(right, cap2);
tangent(cap2, left);
vertical(left);
fix(cap2.center(), [20, 15]);
distance(cap1.center(), cap2.center(), 30);
radius(cap1, 5);
equal(cap1, cap2);
// Five slots in total along world X, 20 apart. The copies register as
// solver entities tied rigidly to their source.
copy("linear", "x", { count: 5, offset: 20 }, left, cap1, right, cap2)
})
extrude(4)
Circular
- Center: the X and Y of the centre point, in sketch coordinates.
- Total Count: the number of instances, the original included.
- Angle (°): Total is the whole sweep, distributed evenly —
360closes the ring; Offset is the angle between neighbours. - Skip lists the instances to leave out, by index.

The code behind it
import { sketch, circle, copy, extrude, origin } from 'fluidcad/core';
import { coincident, diameter, distance } from 'fluidcad/constraints';
// A flange with a six-bolt pattern.
sketch("xy", () => {
const rim = circle([0, 0], 120);
const bore = circle([0, 0], 40);
coincident(rim.center(), origin());
coincident(bore.center(), origin());
diameter(rim, 120);
diameter(bore, 40);
// One bolt hole on the pitch circle (radius 45).
const bolt = circle([45, 0], 10);
diameter(bolt, 10);
distance(bolt.center(), origin(), 45);
// Six holes around a 2D centre point — not an axis, as in 3D.
copy("circular", [0, 0], { count: 6, angle: 360 }, bolt)
})
extrude(8)
By hand
copy("linear", xAxis(), { count: 4, offset: 40 }, g) // along the sketch's X
copy("linear", [xAxis(), yAxis()], { count: 3, offset: 30 }, g) // a grid
copy("linear", axis(l), { count: 4, offset: 40 }, g) // along a sketched line
copy("circular", [0, 0], { count: 8, angle: 360 }, g) // around a 2D point
Options: count (instances including the original), offset (linear spacing) or angle (circular spread), skip (indices to leave out), centered.
Bare axis strings are world axes. On any plane other than XY, use the sketch datums xAxis() / yAxis() for the sketch's own directions — see Mirror.
Copies and constraints
The copies register as solver entities tied rigidly to their source: they move when the source moves, and constraining a copy drives the source. Address one with cp.instance(k):
const cp = copy("linear", "x", { count: 3, offset: 40 }, c)
coincident(cp.instance(2).center(), p) // pins the whole row through the third copy
The 3D copy() outside a sketch duplicates finished solids — see Patterns → Copy.