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 | What the cleaning changed. |
export_meshes
export_meshes(geometry, format: str = 'dotbim') -> strWrite 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
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 | 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
weld_meshes(geometry, *, tolerance_m: Optional[float] = None) -> MeshesResultJoin 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 | 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
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 | 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. |