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:

NameTypeDescription
triangles_in, triangles_outintTriangles before and after.
vertices_in, vertices_outintVertices before and after.
error_mfloat or Nonesimplify_meshes only: the simplifier's error estimate in metres.

MeshesResult

Bases: NamedTuple

The result of weld_meshes or simplify_meshes.

Attributes:

NameTypeDescription
geometrydict{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.
reportdict{name: MeshCounts}.

MeshCleanReport dataclass

What clean_mesh did to one mesh.

Attributes:

NameTypeDescription
vertices_weldedintInput vertices joined into an earlier vertex at the same position.
degenerate_faces_removedintTriangles removed because they repeat a vertex or have zero area.
duplicate_faces_removedintTriangles removed because they exactly repeat an earlier triangle.
faces_flippedintKept triangles whose winding is the reverse of the input winding.
closed_componentsintConnected parts that form a closed surface (turned outward).
open_componentsintConnected parts with open edges; they keep their authored direction.
non_manifold_edgesintEdges shared by three or more triangles.

CleanedMesh

Bases: NamedTuple

The result of clean_mesh: flat arrays and the report.

Attributes:

NameTypeDescription
coordinatesndarrayFlat vertex positions [x, y, z, ...], float32 or float64.
indicesndarrayFlat triangle vertex indices, little-endian uint32.
reportMeshCleanReportWhat 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:

NameTypeDescriptionDefault
geometrydict{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:

TypeDescription
strThe 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:

NameTypeDescriptionDefault
geometrydict{name: {"coordinates": [...], "indices": [...]}} in metres, for example the result of weld_meshes.required
max_error_mfloatError limit in metres. Default: 1.0. math.inf: only target_ratio limits the collapse.None
target_ratiofloatKept share of the triangles, 0 to 1. Default: no count limit. With both limits, the first one met stops the collapse.None
lock_borderboolKeep 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:

TypeDescription
MeshesResultgeometry (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:

TypeDescription
ValueErrorFor 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:

NameTypeDescriptionDefault
geometrydict{name: {"coordinates": [...], "indices": [...]}}, in metres.required
tolerance_mfloatLargest 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:

TypeDescription
MeshesResultgeometry (flat numpy arrays, ready for a payload) and report.

Raises:

TypeDescription
ValueErrorFor 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:

NameTypeDescriptionDefault
coordinatesarray_likeVertex positions, flat [x, y, z, ...] or an (n, 3) array. float64 input stays float64; anything else becomes float32 (the wire precision).required
indicesarray_likeTriangle vertex indices, flat or (m, 3).required

Returns:

TypeDescription
CleanedMeshFlat coordinates and indices arrays and a MeshCleanReport. When nothing changed, the arrays hold the input values.

Raises:

TypeDescription
ValueErrorIf a coordinate is not finite, an index is out of range, or an index is not a whole number in 0..2**32-1.