Selection & Filters
Fillet, chamfer, shell, color, connectors and sketches on faces all need to be told which faces or edges. In the viewport you click them. In the file that click becomes one of three things: an accessor on the feature that made the geometry, a select() with a filter, or a filter passed straight to the operation. This page covers how picking works and what it writes.
Picking in the viewport
- Click a face, edge or vertex to select it. The selection info overlay names what you picked and the feature that produced it.
- Ctrl / Shift + click adds to the selection.
- Right-click a picked entity for the multi-select menu, fetched from the model:
- the tangent chain the edge belongs to (a filleted outline in one go),
- edges or faces of the same type, or of equal measure (all Ø6 holes),
- the producing feature's buckets — Extrude End Edges, Side Faces … — and Select other, which lists the feature's remaining buckets when your pick was, say, a start edge.
- Hovering a menu item previews exactly what clicking it would select.
- With a pick tool armed (Fillet, Chamfer, Shell, Sketch, Offset), the picks go straight into that tool's statement.
A plain click with no tool armed is also the measure gesture.
What the pick writes
The pick tools resolve your clicks to the most stable reference they can:
| Your picks | Written as |
|---|---|
| A whole bucket of one feature (all end edges of an extrude) | fillet(5, e.endEdges()) |
| A subset of a bucket | fillet(5, e.endEdges(0, 2)) — by index |
| Entities a filter describes better | select(edge().onPlane("xy", 30)) then the operation |
| A face for a sketch or connector | sketch(e.endFaces(), …), connector('top', e.endFaces()) |
Feature variables (const e = extrude(30)) are introduced by the writer when a statement needs one.
Direct selection
Every operation returns an object whose accessors pick its own faces and edges:
const e = extrude(30)
fillet(5, e.endEdges()) // top edges
fillet(3, e.startEdges()) // bottom edges
shell(-4, e.endFaces()) // remove top face and hollow
color("blue", e.sideFaces()) // color the sides
By index when there are several:
e.sideFaces(0) // the first side face
e.sideFaces(2) // the third side face
The select function
For more control, use select() with filters from fluidcad/filters:
import { select } from 'fluidcad/core';
import { edge, face } from 'fluidcad/filters';
select(edge().verticalTo("xy"))
fillet(3)
select() finds all matching geometry in the scene and stores it as the last selection. The next operation that needs a selection (fillet, chamfer, shell, color …) uses it and consumes it.
Edge filters
Import edge (and face if using belongsToFace) from fluidcad/filters and chain filter methods. Chained calls AND together:
import { edge, face } from 'fluidcad/filters';
By direction
edge().verticalTo("xy") // edges perpendicular to the XY plane
edge().parallelTo("xz") // edges parallel to the XZ plane
By position
edge().onPlane("xy", 30) // edges that lie on the XY plane at height 30
edge().onPlane("yz", { offset: 50, bothDirections: true }) // edges on YZ plane at offset ±50
edge().above("xy", 10) // edges entirely above z = 10
edge().below("xz", { partial: true }) // edges with at least one end on the −y side of XZ
Relative to another feature
A plane, half-space, or parent-face test can take a feature's face group instead of a number. The reference follows the feature through later edits, so no offset is baked into the selector:
const base = extrude(10);
edge().onPlane(base.endFaces()) // edges on the plane of the base's top faces
edge().above(base.endFaces()) // edges entirely above that plane
edge().above(base.endFaces(), 5) // … offset 5 along its normal
edge().belongsToFace(base.endFaces()) // edges bounding one of those faces (as built)
By rank and extremes
Rank filters compare an edge with the other candidates instead of with a number. They keep the whole tied group — every edge whose center of mass sits at the extreme within tolerance — so a box's farthest("z") is its four top rim edges:
edge().farthest("z") // the topmost layer of edges (by center of mass)
edge().nearest("x") // the layer with the least x
edge().farthest("-y") // negative axes, vectors ([1, 1, 0]) and axes work too
edge().nth("z", 1) // the second layer from the bottom; nth("z", -1) is farthest("z")
edge().largest() // the longest edges (ties included)
edge().smallest("radius") // the circles / arcs of least radius
Chain order is evaluation order: edge().line().farthest("z") ranks only the lines, while edge().farthest("z").line() keeps the lines within the overall top layer.
By convexity
edge().convex() // outer corners — the usual fillet targets
edge().concave() // inner corners — where a boss meets its base
edge().smooth() // tangent transitions, such as a fillet's boundaries
By shape
edge().circle() // circular edges
edge().arc() // arc-shaped edges
edge().line() // straight edges
edge().line(10) // straight edges with length 10
By parent face
edge().belongsToFace(face().circle()) // edges on circular faces
edge().belongsToFace(face().parallelTo("xy")) // edges on faces parallel to XY
// or pass a scene object directly
const topFaces = select(face().onPlane("xy", 30));
edge().belongsToFace(topFaces) // edges belonging to those faces
By source object
Restrict the selection to edges that come from one or more specific scene objects — useful when several solids share the scene:
const sa = sketch("xy", () => {
const b = line([0, 0], [20, 0]);
const r = line([20, 0], [20, 20]);
const t = line([20, 20], [0, 20]);
const l = line([0, 20], [0, 0]);
coincident(b.end(), r.start());
coincident(r.end(), t.start());
coincident(t.end(), l.start());
coincident(l.end(), b.start());
})
const a = extrude(40, sa)
const sb = sketch("xy", () => circle([0, 0], 15))
const b = extrude(40, sb)
select(edge().from(a).line()) // only line edges from `a`
select(edge().from(a, b).circle()) // circle edges from either `a` or `b`
from() composes with the rest of the chain (AND), so edge().from(a).onPlane("xy", 0) returns edges from a that also lie on the XY plane.
One instance of a repeat() pattern is a source object too: select(edge().from(r.instance(1)).circle()) scopes the filter to the second instance of the repeat r. When the repeat clones a single feature, the instance also forwards that feature's accessors — r.instance(1).endEdges() — see Selecting one instance.
Negation
edge().notOnPlane("xy") // edges NOT on the ground plane
edge().notCircle() // edges that aren't circular
edge().notBelongsToFace(face().cylinder()) // edges NOT on cylindrical faces
edge().notFarthest("z") // everything but the top layer
edge().notConvex() // inner corners and smooth transitions only
Face filters
Import face (and edge if using hasEdge) from fluidcad/filters:
import { face, edge } from 'fluidcad/filters';
By direction
face().parallelTo("xy") // faces parallel to the XY plane (top/bottom faces)
face().parallelTo("xz") // faces parallel to the XZ plane (front/back faces)
By position
face().onPlane("xy", 30) // faces on the XY plane at height 30
face().above("xy", 10) // faces entirely above z = 10
face().below(base.endFaces()) // faces entirely below the plane of a feature's face group
By rank and extremes
face().farthest("z") // the top face(s) — the layer with the greatest center z
face().nearest("-x") // the face(s) farthest along −x
face().nth("y", 1) // the second layer of faces from the −y end
face().largest() // the faces of greatest area (ties included)
face().smallest("radius") // the cylinder of least radius
Rank filters evaluate over whatever the chain has kept so far: face().planar().farthest("z") is the topmost planar face.
By shape
face().circle() // circular (flat round) faces
face().cylinder() // cylindrical faces
face().cone() // conical faces
By edge properties
face().edgeCount(4) // faces with exactly 4 edges (rectangular faces)
face().hasEdge(edge().line()) // faces that have at least one straight edge
face().hasEdge(edge().circle(10)) // faces with a circular edge of diameter 10
// or pass a scene object directly
const circularEdges = select(edge().circle());
face().hasEdge(circularEdges) // faces that share an edge with the selection
By source object
select(face().from(a).parallelTo("xy")) // top/bottom faces of `a`
select(face().from(a, b).cylinder()) // cylindrical faces from either object
from() composes with other filters as AND. When the surrounding select() is cloned by repeat() or mirror(), the references are remapped to the cloned objects automatically.
One instance of a pattern is a source object too: select(edge().from(r.instance(1)).circle()) scopes the filter to the second instance of the repeat r. When the repeat clones a single feature, the instance also forwards that feature's accessors — r.instance(1).endEdges() — see Selecting one instance.
Negation
face().notOnPlane("xy", 0) // faces NOT on the ground plane
face().notCircle() // non-circular faces
face().notEdgeCount(4) // faces that don't have exactly 4 edges
face().notHasEdge(edge().arc()) // faces without any arc edges
face().notLargest() // every face but the largest
Using selections
With fillet and chamfer
select(edge().onPlane("xy", 30))
fillet(5) // fillets the selected edges
select(edge().verticalTo("xy"))
chamfer(2) // chamfers vertical edges
With shell
select(face().onPlane("xy", 30))
shell(-3) // removes selected face and hollows the solid
With color
select(face().circle())
color("red") // colors all circular faces red
select(face().parallelTo("xy"))
color("#3498db") // hex colors work too
Passing directly
Filters can also go straight into an operation, or narrow a direct selection:
fillet(5, e.endEdges()) // direct from operation
fillet(5, e.sideFaces(face().cylinder())) // filter within direct selection
Example: selective filleting
Fillet only the vertical edges of a box, then color the top:

The code behind it
import { sketch, extrude, fillet, select, color } from 'fluidcad/core';
import { line } from 'fluidcad/core';
import { edge, face } from 'fluidcad/filters';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";
sketch("xy", () => {
const sg1 = line([-40, -30], [40, -30]);
const sg2 = line([40, -30], [40, 30]);
const sg3 = line([40, 30], [-40, 30]);
const sg4 = line([-40, 30], [-40, -30]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-40, -30]);
distance(sg1.start(), sg1.end(), 80);
distance(sg2.start(), sg2.end(), 60);
})
const e = extrude(40)
// Round only the vertical edges
select(edge().verticalTo("xy"))
fillet(8)
// Color the top face
select(face().onPlane("xy", 40))
color("steelblue")
Selections that must survive
A selection is consumed by the operation that uses it. To feed the same selection to two features, mark it .reusable() — see Reusable objects.