This page documents the standalone 2D sketch solver that powers Sketch mode. The solver is exposed as an npm subpath so you can use it headlessly in Node or inside your own tooling.
import { ConstraintSolver, ConstraintEngine, constraints } from 'brep-io-kernel/SketchSolver2D';ConstraintSolveris the stateful wrapper. It ownssketchObject, provides editing helpers (create/remove geometry and constraints), and calls the engine to solve.ConstraintEngineis the stateless numeric solver. It solves a JSON snapshot and returns a new solved sketch object.constraintDefinitions.jsimplements each constraint type and exposes a globaltolerancethat controls how strictly constraints are considered satisfied.
The engine iterates through constraints in a fixed order, repeatedly nudging points until positions stabilize or the iteration limit is reached. Some geometries inject temporary constraints (for example, arc equal-chord and bezier handle colinearity) that are removed from the final output.
points:{ id:number, x:number, y:number, fixed:boolean }geometries:{ id:number, type:"line"|"circle"|"arc"|"bezier", points:number[], construction?:boolean }constraints:{ id:number, type:string, points:number[], value?:number|null, labelX?:number, labelY?:number, displayStyle?:string }
If you pass an empty sketch, the solver will seed an origin point and a fixed (ground) constraint.
Constraint types are single-character symbol strings. Use the Unicode symbols below in code, or
inspect src/features/sketch/sketchSolver2D/constraintDefinitions.js for the canonical list.
const CONSTRAINTS = {
HORIZONTAL: "━", // 2 points
VERTICAL: "│", // 2 points
DISTANCE: "⟺", // 2 points, value
LINE_TO_POINT_DISTANCE: "↥", // 3 points (line A,B and point C), value
EQUAL_DISTANCE: "⇌", // 4 points
PARALLEL: "∥", // 4 points
PERPENDICULAR: "⟂", // 4 points
ANGLE: "∠", // 4 points, value in degrees
COINCIDENT: "≡", // 2 points
POINT_ON_LINE: "⏛", // 3 points (line AB, point C)
MIDPOINT: "⋯", // 3 points (A, B, midpoint)
FIXED: "⏚", // 1 point
};LINE_TO_POINT_DISTANCE constrains the perpendicular distance from point C to the infinite line through A-B. In the UI this same symbol ↥ is used for the toolbar button and glyph.
Use ↥ as the canonical persisted symbol for this constraint type.
ConstraintSolver is the main class you use in headless workflows.
Constructor:
const solver = new ConstraintSolver({
sketch, // { points, geometries, constraints }
notifyUser, // optional: (message, type) => void
updateCanvas, // optional: () => void
getSelectionItems, // optional: () => Array<{ type:"point"|"geometry", id:number }>
appState, // optional: { mode, type, requiredSelections }
});Common methods:
solver.solveSketch("full"); // or solver.solveSketch(iterations)
solver.pause("reason");
solver.resume();
solver.isPaused();
solver.getPointById(id);
solver.createGeometry("line", [pointA, pointB]);
solver.createConstraint(CONSTRAINTS.DISTANCE, [
{ type: "point", id: pointA.id },
{ type: "point", id: pointB.id },
]);
solver.createConstraint(CONSTRAINTS.LINE_TO_POINT_DISTANCE, [
{ type: "geometry", id: lineId }, // line contributes A and B
{ type: "point", id: pointC.id },
]);
solver.removePointById(id);
solver.removeGeometryById(id);
solver.removeConstraintById(id);Notes:
solveSketchmutatessolver.sketchObjectand also returns the solved sketch.createGeometryandcreateConstraintcan usegetSelectionItemsif you omit points.createConstraintexpects selection items with{ type:"point"|"geometry", id:number }; it mirrors the UI behavior for allowed selections.
Use ConstraintEngine if you want a one-shot solve without any editing helpers.
const engine = new ConstraintEngine(JSON.stringify(sketch));
const solved = engine.solve(500);const solver = new ConstraintSolver({
sketch: {
points: [
{ id: 0, x: 0, y: 0, fixed: true },
{ id: 1, x: 10, y: 5, fixed: false },
],
geometries: [
{ id: 0, type: "line", points: [0, 1], construction: false },
],
constraints: [
{ id: 0, type: CONSTRAINTS.FIXED, points: [0] },
{ id: 1, type: CONSTRAINTS.HORIZONTAL, points: [0, 1] },
{ id: 2, type: CONSTRAINTS.DISTANCE, points: [0, 1], value: 20 },
],
},
});
const solved = solver.solveSketch("full");
console.log(solved.points);const p0 = solver.getPointById(0);
const p1 = solver.getPointById(1);
const p2 = { id: 2, x: 10, y: 0, fixed: false };
const p3 = { id: 3, x: 10, y: 10, fixed: false };
solver.sketchObject.points.push(p2, p3);
solver.sketchObject.geometries.push({ id: 1, type: "line", points: [p2.id, p3.id] });
solver.sketchObject.constraints.push({
id: 3,
type: CONSTRAINTS.PERPENDICULAR,
points: [p0.id, p1.id, p2.id, p3.id],
});
solver.solveSketch(500);const snapshot = JSON.parse(JSON.stringify(solver.sketchObject));
const engine = new ConstraintEngine(JSON.stringify(snapshot));
const solved = engine.solve(250);constraints.tolerance = 1e-5;
constraints.distanceSlideThresholdRatio = 0.10; // only slide distance target updates above 10%
constraints.distanceSlideStepRatio = 0.10; // when sliding, move 10% of remaining gap per solve pass
constraints.distanceSlideMinStep = 0.001; // absolute minimum step while sliding
solver.defaultLoops = () => 1500;
solver.fullSolve = () => 2000;- The solver is iterative. Increase iterations for tighter convergence.
- Constraint errors are stored on the constraint objects as
errorstrings; the solver does not throw. - IDs should be numeric and stable. Constraints reference point IDs by value.
- Some constraint/geometry combinations may not converge; use smaller moves or add grounding constraints.