{"record":{"id":"66b450d5fbe4c752","repo":"stride3d/stride","slug":"tetrahedron-not-in-positive-order","errorCode":null,"errorMessage":"Tetrahedron not in positive order","messagePattern":"Tetrahedron not in positive order","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs","lineNumber":435,"sourceCode":"                // Store additional vertices in extra space\n                // Note: we build 3 extra vertices on a plane with normal (1,1,1) to makes computation easier\n                // As an example, (assuming 2D), if B=(1,1) or (0.5,1.5), we can encompass it with (2,0) and (0,2))\n                var combinedSize = boundingBox.Extent.X + boundingBox.Extent.Y + boundingBox.Extent.Z;\n                vertices[vertices.Length - 4] = boundingBox.Minimum - boundingBox.Extent * 1000.0f;\n                vertices[vertices.Length - 3] = boundingBox.Minimum + Vector3.UnitZ * combinedSize * 1000.0f;\n                vertices[vertices.Length - 2] = boundingBox.Minimum + Vector3.UnitY * combinedSize * 1000.0f;\n                vertices[vertices.Length - 1] = boundingBox.Minimum + Vector3.UnitX * combinedSize * 1000.0f;\n\n                // Create super tetrahedron\n                var superTetrahedron = new Tetrahedron();\n                for (int i = 0; i < 4; ++i)\n                {\n                    superTetrahedron.Vertices[i] = vertices.Length - 4 + i;\n                    superTetrahedron.Neighbours[i] = -1; // No neighbour\n                }\n\n                if (!IsTetrahedronPositiveOrder(vertices, ref superTetrahedron))\n                    throw new InvalidOperationException(\"Tetrahedron not in positive order\");\n\n                tetrahedralization.Add(superTetrahedron);\n            }\n        }\n\n        private unsafe void AddVertex(int vertexIndex)\n        {\n            // Clears reused structures (avoid reallocation every vertex)\n            badTetrahedra.Clear();\n            holeFaces.Clear();\n            edges.Clear();\n\n            var vertex = vertices[vertexIndex];\n\n            fixed (Tetrahedron* tetrahedra = tetrahedralization.Items)\n            {\n                // First, find all the triangles that are no longer valid due to the insertion\n                // 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)\"","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs#L417-L453","documentation":"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').","triggerScenarios":"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.","commonSituations":"A LightProbeGroup with all probes on a single plane, probes at identical positions, or an empty/degenerate group being baked.","solutions":["Ensure probe positions span a non-degenerate 3D volume","Remove duplicate/coincident probe positions","Check that the computed bounding extent has positive size on all axes before tetrahedralizing"],"exampleFix":"// before\nvar tetrahedralization = new BowyerWatsonTetrahedralization(coplanarProbes);\n// after\nif (probes.Distinct().Count() >= 4 && HasPositiveExtent(probes))\n    var tetrahedralization = new BowyerWatsonTetrahedralization(probes);","handlingStrategy":"validation","validationCode":"bool IsNonDegenerate(IEnumerable<Vector3> pts) {\n    var a = pts.Distinct().ToList();\n    if (a.Count < 4) return false;\n    // require points off a common plane\n    var p0 = a[0]; var p1 = a[1]; var p2 = a[2];\n    var n = Vector3.Cross(p1 - p0, p2 - p0);\n    return a.Skip(3).Any(p => Math.Abs(Vector3.Dot(p - p0, n)) > 1e-4f);\n}","typeGuard":null,"tryCatchPattern":"try { new BowyerWatsonTetrahedralization(vertices); }\ncatch (InvalidOperationException e) { logger.Error(e, \"Degenerate probe layout\"); }","preventionTips":["Avoid all-coplanar or duplicated probe positions","Deduplicate vertices before tetrahedralization","Validate minimum bounding extent of probe groups"],"tags":["csharp","geometry","lightprobes"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}