stride3d/stride · error · InvalidOperationException

Tetrahedron not in positive order

Error message

Tetrahedron not in positive order

What it means

During initialization of the super-tetrahedron that encloses all input vertices, BowyerWatsonTetrahedralization verifies the tetrahedron's vertex ordering yields a positive signed volume via IsTetrahedronPositiveOrder. A degenerate or badly ordered vertex set throws InvalidOperationException('Tetrahedron not in positive order').

Solutions

  1. Ensure probe positions span a non-degenerate 3D volume
  2. Remove duplicate/coincident probe positions
  3. Check that the computed bounding extent has positive size on all axes before tetrahedralizing

Example fix

// before
var tetrahedralization = new BowyerWatsonTetrahedralization(coplanarProbes);
// after
if (probes.Distinct().Count() >= 4 && HasPositiveExtent(probes))
    var tetrahedralization = new BowyerWatsonTetrahedralization(probes);
Defensive patterns

Strategy: validation

Validate before calling

bool IsNonDegenerate(IEnumerable<Vector3> pts) {
    var a = pts.Distinct().ToList();
    if (a.Count < 4) return false;
    // require points off a common plane
    var p0 = a[0]; var p1 = a[1]; var p2 = a[2];
    var n = Vector3.Cross(p1 - p0, p2 - p0);
    return a.Skip(3).Any(p => Math.Abs(Vector3.Dot(p - p0, n)) > 1e-4f);
}

Try / catch

try { new BowyerWatsonTetrahedralization(vertices); }
catch (InvalidOperationException e) { logger.Error(e, "Degenerate probe layout"); }

Prevention

When it happens

Trigger: Running tetrahedralization (BowyerWatsonTetrahedralization constructor) on light probe positions where the computed bounding extent is degenerate — e.g. all probes coplanar, duplicated, or with zero extent along an axis.

Common situations: A LightProbeGroup with all probes on a single plane, probes at identical positions, or an empty/degenerate group being baked.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/66b450d5fbe4c752. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs:435

                // Store additional vertices in extra space
                // Note: we build 3 extra vertices on a plane with normal (1,1,1) to makes computation easier
                // As an example, (assuming 2D), if B=(1,1) or (0.5,1.5), we can encompass it with (2,0) and (0,2))
                var combinedSize = boundingBox.Extent.X + boundingBox.Extent.Y + boundingBox.Extent.Z;
                vertices[vertices.Length - 4] = boundingBox.Minimum - boundingBox.Extent * 1000.0f;
                vertices[vertices.Length - 3] = boundingBox.Minimum + Vector3.UnitZ * combinedSize * 1000.0f;
                vertices[vertices.Length - 2] = boundingBox.Minimum + Vector3.UnitY * combinedSize * 1000.0f;
                vertices[vertices.Length - 1] = boundingBox.Minimum + Vector3.UnitX * combinedSize * 1000.0f;

                // Create super tetrahedron
                var superTetrahedron = new Tetrahedron();
                for (int i = 0; i < 4; ++i)
                {
                    superTetrahedron.Vertices[i] = vertices.Length - 4 + i;
                    superTetrahedron.Neighbours[i] = -1; // No neighbour
                }

                if (!IsTetrahedronPositiveOrder(vertices, ref superTetrahedron))
                    throw new InvalidOperationException("Tetrahedron not in positive order");

                tetrahedralization.Add(superTetrahedron);
            }
        }

        private unsafe void AddVertex(int vertexIndex)
        {
            // Clears reused structures (avoid reallocation every vertex)
            badTetrahedra.Clear();
            holeFaces.Clear();
            edges.Clear();

            var vertex = vertices[vertexIndex];

            fixed (Tetrahedron* tetrahedra = tetrahedralization.Items)
            {
                // First, find all the triangles that are no longer valid due to the insertion
                // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)"

View on GitHub (pinned to 96fad776d2)