---
title: Geometry
source: https://infrared.city/docs/sdk/1.1/python/geometry/
---

# Geometry

Mesh utilities for bring-your-own geometry.

`clean_mesh` repairs one mesh: it joins duplicate vertices, drops degenerate and duplicate triangles, and makes the triangle winding consistent and outward-facing.

To make `context_geometry` light: `weld_meshes` joins the corners that an export wrote once per triangle, `simplify_meshes` makes a low-resolution copy, and `export_meshes` writes a geometry dict as a dotbim, OBJ or SDK JSON file, so you can inspect it and keep it. The results of weld and simplify go straight into a payload; no file is needed.

Notes

You do not need it for correct results: the SDK already cleans every building before it grids facades and roofs (unless a payload sets `mesh_cleaning="off"`). Use it to see what the cleaning does to your mesh, or to make an upload smaller (an unwelded export carries every shared corner once per triangle).

## MeshCounts  `dataclass`

What `weld_meshes` or `simplify_meshes` did to one mesh.

Attributes:

| Name | Type | Description |
| --- | --- | --- |
| `triangles_in, triangles_out` | `int` | Triangles before and after. |
| `vertices_in, vertices_out` | `int` | Vertices before and after. |
| `error_m` | `float or None` | `simplify_meshes` only: the simplifier's error estimate in metres. |

## MeshesResult

Bases: `NamedTuple`

The result of `weld_meshes` or `simplify_meshes`.

Attributes:

| Name | Type | Description |
| --- | --- | --- |
| `geometry` | `dict` | `{name: {"coordinates": numpy.ndarray, "indices": numpy.ndarray}}`, flat arrays (no copy). Pass it straight on as `context_geometry`, `geometries` or `ground_geometry`, or to the next step. |
| `report` | `dict` | `{name: MeshCounts}`. |

## MeshCleanReport  `dataclass`

What `clean_mesh` did to one mesh.

Attributes:

| Name | Type | Description |
| --- | --- | --- |
| `vertices_welded` | `int` | Input vertices joined into an earlier vertex at the same position. |
| `degenerate_faces_removed` | `int` | Triangles removed because they repeat a vertex or have zero area. |
| `duplicate_faces_removed` | `int` | Triangles removed because they exactly repeat an earlier triangle. |
| `faces_flipped` | `int` | Kept triangles whose winding is the reverse of the input winding. |
| `closed_components` | `int` | Connected parts that form a closed surface (turned outward). |
| `open_components` | `int` | Connected parts with open edges; they keep their authored direction. |
| `non_manifold_edges` | `int` | Edges shared by three or more triangles. |

## CleanedMesh

Bases: `NamedTuple`

The result of `clean_mesh`: flat arrays and the report.

Attributes:

| Name | Type | Description |
| --- | --- | --- |
| `coordinates` | `ndarray` | Flat vertex positions `[x, y, z, ...]`, `float32` or `float64`. |
| `indices` | `ndarray` | Flat triangle vertex indices, little-endian `uint32`. |
| `report` | `[MeshCleanReport](#infrared_sdk.geometry.MeshCleanReport)` | What the cleaning changed. |

## export_meshes

```python
export_meshes(geometry, format: str = 'dotbim') -> str
```

Write a geometry dict as text, to inspect or keep it.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `geometry` | `dict` | `{name: {"coordinates": [...], "indices": [...]}}`, for example `simplify_meshes(...).geometry`. A mesh may carry `"type"` (a string): the dotbim element type, default `"Context"`. | *required* |
| `format` | `('dotbim', 'obj', 'json')` | `"dotbim"` (default): the open JSON mesh format of the Grasshopper, Blender and Speckle plugins, one element per name; its `info` says metres, Z up and the site local frame. `"obj"`: Wavefront OBJ, one object per name; Rhino opens it without a plugin. `"json"`: the SDK geometry dict; `json.load` it and pass it as `context_geometry`. | `"dotbim"` |

Returns:

| Type | Description |
| --- | --- |
| `str` | The file text. Numbers are written so that they read back exactly. |

## simplify_meshes

```python
simplify_meshes(
geometry,
*,
max_error_m: Optional[float] = None,
target_ratio: Optional[float] = None,
lock_border: Optional[bool] = None,
) -> MeshesResult
```

Make a low-resolution copy of every mesh of a geometry dict.

One call for the whole dict; each mesh is simplified on its own (meshes never join, so the per-tile context selection still sees each one). For one mesh, pass a dict with one entry. Each mesh is welded exactly first (use `weld_meshes` before it for a tolerance), then edges collapse in the order of the smallest shape change (quadric error) until a limit stops them. No vertex moves: the copy keeps a subset of the input vertices.

Warnings

`max_error_m` is a typical error, not a bound. On real terrain and buildings most of the surface stays within it, but single points can be 2 to 3 times as far, and a thin part (a mast, an antenna) becomes a line.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `geometry` | `dict` | `{name: {"coordinates": [...], "indices": [...]}}` in metres, for example the result of `weld_meshes`. | *required* |
| `max_error_m` | `float` | Error limit in metres. Default: 1.0. `math.inf`: only `target_ratio` limits the collapse. | `None` |
| `target_ratio` | `float` | Kept share of the triangles, `0` to `1`. Default: no count limit. With both limits, the first one met stops the collapse. | `None` |
| `lock_border` | `bool` | Keep every open edge (default `True`), so the rim of a terrain sheet still meets its neighbour. Set `False` for buildings with an open base: they then simplify further. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `[MeshesResult](#infrared_sdk.geometry.MeshesResult)` | `geometry` (flat numpy arrays, ready for `context_geometry`) and `report` (counts and the error estimate per mesh). A mesh that collapses to no triangle (a thin box with `lock_border=False`) is left out of both: `len(geometry)` can be less than the input count. |

Raises:

| Type | Description |
| --- | --- |
| `ValueError` | For a non-finite coordinate, an index out of range, or a limit out of range. |

## weld_meshes

```python
weld_meshes(geometry, *, tolerance_m: Optional[float] = None) -> MeshesResult
```

Join the duplicate corners of every mesh of a geometry dict.

Many exports (Grasshopper, Rhino, and others) write each triangle with its own three corners, and the copies of one corner are not always bit-identical. The weld joins a vertex to the first earlier vertex that is at most `tolerance_m` away, then drops the triangles that became degenerate and exact duplicates. No vertex moves, the winding is kept, and meshes never join each other. The result is smaller and has the same shape.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `geometry` | `dict` | `{name: {"coordinates": [...], "indices": [...]}}`, in metres. | *required* |
| `tolerance_m` | `float` | Largest distance between two copies of one corner, in metres. The default (`None`) is 0: only bit-identical positions join. Keep it well below the smallest real edge of the model. | `None` |

Returns:

| Type | Description |
| --- | --- |
| `[MeshesResult](#infrared_sdk.geometry.MeshesResult)` | `geometry` (flat numpy arrays, ready for a payload) and `report`. |

Raises:

| Type | Description |
| --- | --- |
| `ValueError` | For a non-finite coordinate, an index out of range, or a negative tolerance. |

## clean_mesh

```python
clean_mesh(coordinates, indices) -> CleanedMesh
```

Clean one mesh the way the SDK cleans each building.

The steps: join vertices at bit-identical positions (`-0.0` equals `+0.0`, no tolerance), drop degenerate and exact duplicate triangles, make the winding consistent (the authored majority wins), and turn closed parts outward. An open part keeps its authored direction.

Warnings

Clean each building on its own: two objects that touch must stay apart.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `coordinates` | `array_like` | Vertex positions, flat `[x, y, z, ...]` or an `(n, 3)` array. `float64` input stays `float64`; anything else becomes `float32` (the wire precision). | *required* |
| `indices` | `array_like` | Triangle vertex indices, flat or `(m, 3)`. | *required* |

Returns:

| Type | Description |
| --- | --- |
| `[CleanedMesh](#infrared_sdk.geometry.CleanedMesh)` | Flat `coordinates` and `indices` arrays and a `MeshCleanReport`. When nothing changed, the arrays hold the input values. |

Raises:

| Type | Description |
| --- | --- |
| `ValueError` | If a coordinate is not finite, an index is out of range, or an index is not a whole number in `0..2**32-1`. |
