# Check dimensions

Claim a bounding box, read the measured bounds and pin where the part sits.

Canonical page: https://www.geospec.xyz/docs/guides/check-dimensions

`toHaveBoundingBox` compares the axis-aligned bounds of a subject with the values you expect. It is the first claim most specs make, and the one the [quick start](https://www.geospec.xyz/docs.mdx) uses.

### 1. Claim one axis [#1-claim-one-axis]

```ts
expectGeo(part)
  .toHaveBoundingBox({ size: { x: 10 } });
```

Expect only the axes you care about: this claim checks the width of the 10 × 20 × 30 mm box and leaves height and depth free. Set `x` to 12 and the claim fails with `GEOSPEC_BOUNDING_BOX_MISMATCH`; add `y: 20` to check a second axis.

### 2. Read the measurement [#2-read-the-measurement]

A passed or failed claim carries its evidence: the measured `min`, `max`, `center` and `size`, the expectation the engine applied, and the source it measured, `brep` for STEP and `mesh` for triangles.

**TypeScript**

The assertion `expectGeo` returns holds it under `report.evidence`. A failed or refused chain throws `GeoSpecAssertionError` with the same report under `error.report`, and `error.diagnostics[0].code` names the reason.

**Python**

The report `expect_geo` returns holds it under `report.result["evidence"]`. A failed or refused claim raises `GeoSpecAssertionError` with the same report under `error.result`, and `error.code` names the reason.

The box above measures as:

```json
{
  "measured": { "center": [5, 10, 15], "max": [10, 20, 30], "min": [0, 0, 0], "size": [10, 20, 30] },
  "normalizedExpected": { "size": { "x": 10 }, "tolerance": 0.02 },
  "witnesses": { "axisFailures": [], "primitives": [], "source": "brep", "tolerance": 0.02 }
}
```

A failed claim lists each field that missed under `axisFailures`, in the diagnostic's `details` and in the witnesses, with its expected and actual values.

### 3. Pin the position [#3-pin-the-position]

```ts
expectGeo(part)
  .toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
expectGeo(part)
  .toHaveBoundingBox([0, 0, 0], [40, 40, 20]);
expectGeo(part)
  .toHaveBoundingBox({ center: { x: 0, y: 0 } });
```

`size` checks extents and ignores where the part sits; two vectors, `min` and `max`, check position as well; `center` fixes position with one point, here on x and y only. The plate is 40 × 40 × 20 mm and centred on the z axis, so the second claim fails on four fields, `min.x`, `min.y`, `max.x` and `max.y`: change the vectors to `[-20, -20, 0]` and `[20, 20, 20]` and it passes.

### 4. Claim a mesh [#4-claim-a-mesh]

```ts
expectGeo(part)
  .toHaveBoundingBox({ size: { z: 5 } });
```

The claim reads the same on the gear, a GLB mesh, and the evidence `source` is `mesh`. Its 624 triangles span 4.999976634979248 mm in z, inside the default 0.02 mm tolerance: add `tolerance: 0` beside `size` and the claim fails on `size.z`. A mesh claim charges work units per triangle; [Set tolerances and budgets](https://www.geospec.xyz/docs/guides/set-tolerances-and-budgets.mdx) shows this claim refused under a small budget, and states the tolerance rule.

## Variations [#variations]

**A negated claim.** `.not` in TypeScript and `.not_` in Python ask the engine to show the bounds are not as declared: on the box, `not.toHaveBoundingBox({ size: { x: 10 } })` fails with `GEOSPEC_NEGATED_MATCH` and passes once `x` is 12.

**Combined and empty expectations.** `size: { x: 40 }` with `center: { z: 10 }` checks one extent and one position on the plate. An expectation with no bound refuses before anything is measured, with `GEOSPEC_INVALID_EXPECTATION`.

**Units.** A STEP file declares its unit. A GLB does not, so load a mesh with `sourceUnit: 'mm'` (`source_unit="mm"` in Python); without it the gear is read in metres and its z extent measures 4999.976634979248.

**Mass and area.** Bounds say nothing about material inside them. [Check mass properties](https://www.geospec.xyz/docs/guides/check-mass-properties.mdx) measures volume, area and centre of mass; the [measurement reference](https://www.geospec.xyz/docs/reference/measurement.mdx#tohaveboundingbox) lists every field.