> ## Documentation Index
> Fetch the complete documentation index at: https://invariant-cad.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sketches and constraints

> Author planar profiles with explicit entities, loops, holes, and the built-in constraint solver.

Sketches define planar profiles for extrusion, revolution, lofting, and sweeps.
The callback receives a `SketchBuilder` and must return exactly one
`sketch.profile(...)`.

## Planes

```ts theme={"system"}
const xy = plane.xy();
const elevated = plane.xy(vec3(mm(0), mm(0), mm(30)));
const yz = plane.yz();
const xz = plane.xz();
```

Document 0.1 supports the three principal plane orientations with an optional
parameterized origin.

## A profile with holes

```ts theme={"system"}
const profile = cad.sketch("flange-profile", plane.xy(), (sketch) => {
  const outline = sketch.rectangle("outline", {
    width: mm(80),
    height: mm(50),
  });
  const left = sketch.circle("left-hole", {
    center: vec2(mm(-25), mm(0)),
    radius: mm(4),
  });
  const right = sketch.circle("right-hole", {
    center: vec2(mm(25), mm(0)),
    radius: mm(4),
  });

  return sketch.profile(outline, {
    holes: [left.loop(), right.loop()],
  });
});
```

Profiles have one explicit outer loop and zero or more explicit hole loops. The
evaluator does not infer which disconnected loop is outer from enumeration
order. These roles are explicit modeling intent: traversal winding does not
turn a declared hole into an outer loop or an outer loop into a hole. Profiles
do not currently express islands or general multi-region region algebra.

## Entities

`SketchBuilder` supports:

* `point(id, position)`
* `line(id, start, end)`
* `circle(id, { center, radius })`
* `arc(id, { center, radius, startAngle, endAngle })`
* `polyline(id, points, { closed })`
* `rectangle(id, options)`
* `regularPolygon(id, sides, radius, options)`

Use `sketch.loop([...])` to assemble ordered lines and arcs. Reverse an edge use
when the loop traversal runs opposite its authored direction. A loop must be
closed and non-self-intersecting after solving.

## Constraint example

```ts theme={"system"}
const profile = cad.sketch("constrained-profile", plane.xy(), (sketch) => {
  const a = sketch.point("a", vec2(mm(0), mm(0)));
  const b = sketch.point("b", vec2(mm(40), mm(0)));
  const c = sketch.point("c", vec2(mm(40), mm(20)));
  const d = sketch.point("d", vec2(mm(0), mm(20)));

  const bottom = sketch.line("bottom", a, b);
  const right = sketch.line("right", b, c);
  const top = sketch.line("top", c, d);
  const left = sketch.line("left", d, a);

  sketch
    .fixed("anchor", a)
    .horizontal("bottom-horizontal", bottom)
    .horizontal("top-horizontal", top)
    .vertical("right-vertical", right)
    .vertical("left-vertical", left)
    .length("width", bottom, mm(40))
    .length("height", left, mm(20));

  return sketch.profile(sketch.loop([bottom, right, top, left]));
});
```

## Supported constraints

| Category    | Constraints                                          |
| ----------- | ---------------------------------------------------- |
| Position    | coincident, fixed, midpoint                          |
| Orientation | horizontal, vertical, parallel, perpendicular, angle |
| Distance    | point distance, X distance, Y distance, line length  |
| Equality    | equal length, equal radius                           |
| Curves      | radius, diameter, line-circle tangent                |

The built-in solver is a permissively licensed reference implementation for
0.1. It is replaceable through the sketch-solver protocol. Industrial conflict
isolation, redundant-constraint explanations, drag solving, and large sparse
systems remain roadmap items.

## Tolerance and validity

```ts theme={"system"}
cad.sketch("precision-profile", plane.xy(), buildProfile, {
  tolerance: 1e-7,
});
```

Tolerance is a positive finite number in millimetres. It affects sketch
solution and profile validity checks; it is not permission to accept arbitrary
gaps.

After the final call-time parameter and named-configuration values are
resolved, the evaluator validates the solved profile before handing it to a
dependent geometry-kernel feature. When a profile declares holes, its resolved
loops must be finite, nondegenerate, closed, and simple enough for the current
single-outer-region contract. Every hole boundary must have clearance strictly
greater than the sketch tolerance from the outer boundary and every other hole
boundary. A hole that is disjoint but outside the outer loop, encloses it,
touches it, crosses or partially overlaps it, or coincides with it is rejected.
Distinct holes likewise cannot touch, intersect, coincide, or nest. After the
same bounded and cancellable closure gate used for every solved profile,
hole-free profiles retain their existing feature-specific geometric admission
and diagnostics.

This evaluator-side hole-region decision is kernel-neutral, so Manifold, OCCT,
and a custom kernel used through `Evaluator` receive the same decision. Invalid
regions fail before the profile reaches a dependent kernel call with
`SKETCH_NO_CLOSED_REGION`. Relationship and self-contact diagnostics identify
the sketch and resolved loop index where available, and retain an implicated
entity only when solver provenance exactly matches the authored loop and
curve. Closure failures retain the established profile-scoped diagnostic.
Explicit outer/hole roles remain independent of clockwise or counterclockwise
traversal.

The validator uses analytic line, arc, and circle predicates plus adaptive
chord-sagitta bounds for separation proofs. Curve `segments` values are
rendering/backend tessellation hints and do not relax or discretize this
decision. The closure and hole-region checks share a cumulative work ceiling
per sketch, sourced from
`DEFAULT_DESIGN_DOCUMENT_LIMITS.maxStructuralValues`, and the evaluation abort
signal. Exhausting the ceiling returns `RESOURCE_LIMIT_EXCEEDED`, cancellation
returns `EVALUATION_ABORTED`, and a numerically uncertain clearance fails
closed.

This guarantee belongs to document evaluation. A direct low-level
`GeometryKernel` profile call bypasses the evaluator boundary and must satisfy
the kernel's resolved-profile contract itself. InvariantCAD does not clamp,
move, or reinterpret an invalid hole, and it does not derive relational
parameter bounds automatically.

## Stable entity IDs

Entity IDs are later available as sketch-source topology provenance. If a
fillet intends the extrusion side created by `outline.e1`, select that source
explicitly rather than relying on geometric position. Preserve entity IDs when
refactoring a sketch if downstream topology intent should remain stable.
