rf-geom / ridgeforge Public

limits and robustness

Where it stops working, and how it tells you

A geometry library that claims to handle anything is lying about something. This page is the list of the places where RidgeForge gives up, degrades, or produces an answer that is correct but not the one you expected.

Hard limits

QuantityLimitBeyond it
Vertices per mesh2³¹ − 1validate() returns OutOfRange; the index type is 32-bit and the top bit is reserved as a marker
Triangles per mesh2³¹ − 1same, and you would need 25 GB just for the arrays
Coordinate magnitude10¹⁸quadric entries square the coordinates and overflow to infinity; the collapse order becomes arbitrary
Recommended magnitude10⁴above this, single precision loses the resolution that the error ceiling assumes
Boundary loop for fill_holes~5 000 edgesno refusal, but ear clipping is quadratic and a 20 000-edge loop takes minutes
Componentsunboundednothing special; a million one-triangle components works and is slow for other reasons
Attribute sets1 UV, 1 normaladditional sets are dropped on read without a warning
Normalise before you decimate

Move the mesh so its bounding box is centred on the origin and scale it into a unit cube, decimate, then undo both. A terrain tile stored in projected metres with a 6-digit easting spends most of its float mantissa on an offset nobody cares about, and the error ceiling you pass then means something different on each side of the model. Two lines of code buy a great deal of predictability.

Degenerate input

Nothing here throws, nothing here silently corrupts. Each case has a defined behaviour:

InputWhat happens
NaN or infinite coordinatevalidate() reports it. Every other operation treats it as data: a NaN propagates into the quadric and that vertex stops being collapsible, which usually looks like a frozen patch.
Zero-area triangleIts plane is undefined, so it contributes nothing to any quadric. It is never collapsed away by itself; Repair::remove_degenerate is the tool for it.
Triangle with a repeated indexSame as zero area, and it also breaks the link condition for its edges, which blocks nearby collapses. Remove it first.
Duplicate triangle, same windingDoubles the quadric weight of its region, which biases decimation towards keeping it. Harmless otherwise.
Duplicate triangle, opposite windingMakes the mesh non-orientable at those edges. orient_consistently fails with NonManifold; detection of intersections reports the pair.
Edge with three or more facesDecimation refuses collapses there and continues elsewhere. Stats::non_manifold_edges counts them.
Vertex joining two conesCounted in non_manifold_vertices. Collapsing across it is blocked by the link condition, so it survives decimation intact.
Unreferenced vertexKept. No operation deletes vertices that no triangle uses; Repair::weld_vertices will compact them away only if another vertex is within tolerance.
Empty meshOperations return EmptyMesh rather than succeeding vacuously.

Precision

The default scalar is float. That is enough for the geometry and not quite enough for the quadrics, which is why the quadric accumulation runs in double regardless of the scalar setting, and only the resulting optimal position is rounded back. Without that, a mesh with a million triangles accumulates visible drift in the flat regions.

Building with RF_DOUBLE_PRECISION=ON changes positions, not predicates: the intersection tests were already exact and do not improve. What improves is the stability of the Cholesky solve on nearly singular quadrics, which matters for meshes with very long thin triangles. The cost is 1.9× the memory for positions and roughly 15 % on decimation time.

A test you can run in a minute

Decimate the same mesh twice, once at the original scale and once scaled by 1000 with the error ceiling scaled by 10⁶, then compare triangle counts. If they differ by more than a fraction of a percent, your model sits in a numerically awkward range and normalising will pay for itself.

Memory

Everything is a flat vector, so the arithmetic is easy to do in advance. Single precision, 64-bit platform:

StructureBytesPerPresent during
positions12vertexalways
triangles12trianglealways
normals, uv12, 8vertexonly if populated
Vertex quadrics40vertexdecimation
Triangle adjacency12triangledecimation, remeshing
Collapse heap entry28candidate edgedecimation
Hierarchy node32node, ≈ triangles ÷ 4intersection, projection
Hierarchy index4triangleintersection, projection

A two-million-triangle mesh with a million vertices therefore needs about 36 MB resident, and decimating it peaks near 190 MB, dominated by the three million heap entries. Searching the same mesh for self-intersections peaks near 72 MB, because the hierarchy is cheaper than the heap. The measured figures are in the benchmark tables and they match this arithmetic to within a few percent.

What is not promised

Known issues

Open issues carry an identifier that is stable across releases, so you can refer to one in a message and get a useful reply.

IdIssueStateSince
RF-41resolve makes no progress on two shells nested one inside the other and exhausts its passesopen0.9.2
RF-57fill_holes may produce a self-intersecting patch when a loop is far from planar and the centroid fan is usedopen0.6.0
RF-63the OBJ reader rejects negative, relative face indices instead of resolving themopen0.5.0
RF-68welding with a tolerance larger than the smallest feature merges a UV seam into one vertex; by design, listed because it surprises peopleas designed0.6.0
RF-72the remesher dropped components of two triangles or fewerfixed in 0.9.40.9.0
RF-74PLY files that store coordinates as double are reported as UnsupportedFormat rather than being narrowedopen0.5.0

Reporting something new

Send it to dev@ridgeforge.dev. What makes a report actionable, in order of usefulness:

  1. The smallest mesh that still shows it. Four triangles beats four million, and rfmesh check often finds the offending region for you.
  2. The exact option values, ideally as the rfmesh command line that reproduces it.
  3. Version, compiler and whether the build has RF_DOUBLE_PRECISION or sanitizers on.
  4. What you expected, stated as a property rather than as an appearance.

Reports that arrive with a four-triangle reproducer usually get an answer in a day. Reports that say decimation looks wrong on a model that cannot be shared usually do not get a useful answer at all, through no fault of either side.