public static void CheckForInvalidUnusedEdges(NativeHull hull)
 {
     for (int j = 0; j < hull.EdgeCount; j++)
     {
         ValidateEdge(hull, j);
     }
 }
    public static void ValidateFace(NativeHull hull, int faceIndex)
    {
        if (faceIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(faceIndex));
        }

        NativeFace face = hull.GetFace(faceIndex);

        Debug.Assert(face.Edge >= 0,
                     "All faces should point to a starting edge index");

        Debug.Assert(face.Edge < hull.EdgeCount,
                     "A face references an out of range edge index");

        NativeHalfEdge startEdge = hull.GetEdge(face.Edge);
        NativeHalfEdge current   = startEdge;

        int currentIndex = face.Edge;
        var edgeCount    = 0;

        do
        {
            var next = hull.GetEdge(current.Next);

            Debug.Assert(faceIndex == current.Face,
                         "All edges in a face loop should point to the same face");

            Debug.Assert(currentIndex == next.Prev,
                         "Next and previous edges in a face loop should point to each other");

            ValidateEdge(hull, currentIndex);

            if (++edgeCount >= hull.EdgeCount)
            {
                Debug.Assert(true, "Infinite loop in face edges");
                break;
            }

            currentIndex = current.Next;
            current      = next;
        }while (current.Origin != startEdge.Origin);

        Debug.Assert(edgeCount > 1,
                     "Faces should have more than one edge");
    }
    public static unsafe void CheckAllVerticesAreUsed(NativeHull hull)
    {
        for (int i = 0; i < hull.VertexCount; i++)
        {
            bool isUsed = false;
            for (int j = 0; j < hull.EdgeCount; j++)
            {
                NativeHalfEdge edge = hull.GetEdge(j);
                if (edge.Origin == i)
                {
                    isUsed = true;
                    break;
                }
            }

            Debug.Assert(isUsed,
                         "All vertices should be used by an edge");
        }
    }
    public unsafe static void ValidateHull(NativeHull hull)
    {
        Debug.Assert(hull.IsCreated);
        Debug.Assert(!hull.IsDisposed);

        Debug.Assert((IntPtr)hull.Faces != IntPtr.Zero);
        Debug.Assert((IntPtr)hull.Vertices != IntPtr.Zero);
        Debug.Assert((IntPtr)hull.Planes != IntPtr.Zero);
        Debug.Assert((IntPtr)hull.Faces != IntPtr.Zero);
        Debug.Assert((IntPtr)hull.Edges != IntPtr.Zero);

        for (int i = 0; i < hull.FaceCount; i++)
        {
            ValidateFace(hull, i);
        }

        CheckAllVerticesAreUsed(hull);
        CheckForInvalidUnusedEdges(hull);
    }
    public static void ValidateEdge(NativeHull hull, int edgeIndex)
    {
        if (edgeIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(edgeIndex));
        }

        var edge = hull.GetEdge(edgeIndex);

        if (edge.Twin == -1 || edge.Prev == -1 || edge.Next == -1)
        {
            Debug.LogError($"Edge {edgeIndex} has an out of range index for twin, prev or next");

            // Avoid exceptions so our debug visualizations can aid in debugging the issue.
            return;
        }

        var twin = hull.GetEdge(edge.Twin);

        Debug.Assert(edgeIndex == twin.Twin,
                     "The twin of the edge twin must be the edge itself");

        Debug.Assert(math.abs(edge.Twin - edgeIndex) == 1,
                     "An edge/twin combination should be indexed one directly after the other.");

        Debug.Assert(edgeIndex == hull.GetEdge(edge.Prev).Next,
                     "The previous edge should point to the next edge");

        Debug.Assert(edgeIndex == hull.GetEdge(edge.Next).Prev,
                     "The next edge should point to the previous edge");

        Debug.Assert(edge.Origin != twin.Origin,
                     "Edges and their twin should not point to the same vertex");

        Debug.Assert(edge.Face >= 0,
                     "All edges should have a face index");

        Debug.Assert(edge.Face < hull.FaceCount,
                     "An edge references an out of range face index");
    }
 public ref NativeHalfEdge GetFirstEdge(NativeHull hull) => ref hull.GetEdgeRef(Edge);
    public void DrawHullCollision(GameObject a, GameObject b, RigidTransform t1, NativeHull hull1, RigidTransform t2, NativeHull hull2)
    {
        var collision = HullCollision.GetDebugCollisionInfo(t1, hull1, t2, hull2);

        if (collision.IsColliding)
        {
            if (DrawIntersection) // Visualize all faces of the intersection
            {
                HullIntersection.DrawNativeHullHullIntersection(t1, hull1, t2, hull2);
            }

            if (DrawContact || LogContact)  // Visualize the minimal contact calcluation for physics
            {
                //var manifold = HullOperations.GetContact.Invoke(t1, hull1, t2, hull2);

                var sw1          = System.Diagnostics.Stopwatch.StartNew();
                var tmp          = new NativeManifold(Allocator.Persistent);
                var normalResult = HullIntersection.NativeHullHullContact(ref tmp, t1, hull1, t2, hull2);
                sw1.Stop();
                tmp.Dispose();

                var sw2         = System.Diagnostics.Stopwatch.StartNew();
                var burstResult = HullOperations.TryGetContact.Invoke(out NativeManifold manifold, t1, hull1, t2, hull2);
                sw2.Stop();

                if (LogContact)
                {
                    Debug.Log($"GetContact between '{a.name}'/'{b.name}' took: {sw1.Elapsed.TotalMilliseconds:N4}ms (Normal), {sw2.Elapsed.TotalMilliseconds:N4}ms (Burst)");
                }

                if (DrawContact && burstResult)
                {
                    // Do something with manifold

                    HullDrawingUtility.DebugDrawManifold(manifold);

                    //var points = manifold.Points;

                    for (int i = 0; i < manifold.Length; i++)
                    {
                        var point = manifold[i];
                        DebugDrawer.DrawSphere(point.Position, 0.02f);
                        DebugDrawer.DrawArrow(point.Position, manifold.Normal * 0.2f);

                        var penentrationPoint = point.Position + manifold.Normal * point.Distance;
                        DebugDrawer.DrawLabel(penentrationPoint, $"{point.Distance:N2}");

                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.InEdge1, t1, hull1);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.OutEdge1, t1, hull1);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.InEdge2, t1, hull1);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.OutEdge2, t1, hull1);

                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.InEdge1, t2, hull2);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.OutEdge1, t2, hull2);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.InEdge2, t2, hull2);
                        HullDrawingUtility.DrawEdge(point.Id.FeaturePair.OutEdge2, t2, hull2);

                        DebugDrawer.DrawDottedLine(point.Position, penentrationPoint);
                    }

                    manifold.Dispose();
                }
            }

            if (DrawIsCollided)
            {
                DebugDrawer.DrawSphere(t1.pos, 0.1f, UnityColors.GhostDodgerBlue);
                DebugDrawer.DrawSphere(t2.pos, 0.1f, UnityColors.GhostDodgerBlue);
            }
        }

        if (DrawClosestFace)
        {
            var color1 = collision.Face1.Distance > 0 ? UnityColors.Red.ToOpacity(0.3f) : UnityColors.Yellow.ToOpacity(0.3f);
            HullDrawingUtility.DrawFaceWithOutline(collision.Face1.Index, t1, hull1, color1, UnityColors.Black);

            var color2 = collision.Face2.Distance > 0 ? UnityColors.Red.ToOpacity(0.3f) : UnityColors.Yellow.ToOpacity(0.3f);
            HullDrawingUtility.DrawFaceWithOutline(collision.Face2.Index, t2, hull2, color2, UnityColors.Black);
        }
    }