Пример #1
0
        public static float4 ConvexQuad(Allocator allocator, NativeArray <float2> points, NativeArray <int2> edges, ref NativeArray <float2> outVertices, ref int outVertexCount, ref NativeArray <int> outIndices, ref int outIndexCount, ref NativeArray <int2> outEdges, ref int outEdgeCount)
        {
            // Inputs are garbage, just early out.
            float4 ret = float4.zero;

            outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0;
            if (points.Length < 3 || points.Length >= kMaxVertexCount)
            {
                return(ret);
            }

            // Ensure inputs form a proper PlanarGraph.
            int pgEdgeCount = 0, pgPointCount = 0;
            NativeArray <int2>   pgEdges = new NativeArray <int2>(kMaxEdgeCount, allocator);
            NativeArray <float2> pgPoints = new NativeArray <float2>(kMaxVertexCount, allocator);

            // Valid Edges and Paths, correct the Planar Graph. If invalid create a simple convex hull rect.
            GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, true);
            Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref outVertices, ref outVertexCount, ref outIndices, ref outIndexCount);

            // Dispose Temp Memory.
            pgPoints.Dispose();
            pgEdges.Dispose();
            return(ret);
        }
Пример #2
0
        public static float4 Tessellate(Allocator allocator, NativeArray <float2> points, NativeArray <int2> edges, ref NativeArray <float2> outVertices, ref int outVertexCount, ref NativeArray <int> outIndices, ref int outIndexCount, ref NativeArray <int2> outEdges, ref int outEdgeCount)
        {
            // Inputs are garbage, just early out.
            outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0;
            if (points.Length == 0 || points.Length >= kMaxVertexCount)
            {
                return(float4.zero);
            }

            // Ensure inputs form a proper PlanarGraph.
            bool                 validGraph = false;
            int                  pgEdgeCount = 0, pgPointCount = 0;
            float4               ret      = float4.zero;
            NativeArray <int2>   pgEdges  = new NativeArray <int2>(kMaxEdgeCount, allocator);
            NativeArray <float2> pgPoints = new NativeArray <float2>(kMaxVertexCount, allocator);

            // Valid Edges and Paths, correct the Planar Graph. If invalid create a simple convex hull rect.
            validGraph = PlanarGraph.Validate(allocator, points, points.Length, edges, edges.Length, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount);
            if (!validGraph)
            {
                GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount);
            }

            // Do a proper Delaunay Triangulation.
            int tsIndexCount = 0, tsVertexCount = 0;
            NativeArray <int>    tsIndices = new NativeArray <int>(kMaxIndexCount, allocator);
            NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator);

            validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount);
            if (!validGraph)
            {
                // Create a simplex convex hull rect.
                GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount);
                tsIndexCount = 0; tsVertexCount = 0;
                Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount);
            }

            // Copy Out
            TransferOutput(pgEdges, pgEdgeCount, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount);

            // Dispose Temp Memory.
            tsVertices.Dispose();
            tsIndices.Dispose();
            pgPoints.Dispose();
            pgEdges.Dispose();
            return(ret);
        }
Пример #3
0
        internal static bool Tessellate(Allocator allocator, NativeArray <float2> pgPoints, int pgPointCount, NativeArray <int2> pgEdges, int pgEdgeCount, ref NativeArray <float2> outputVertices, ref int vertexCount, ref NativeArray <int> outputIndices, ref int indexCount)
        {
            // Process.
            Tessellator tess = new Tessellator();

            tess.SetAllocator(allocator);
            int maxCount = 0, triCount = 0;
            var valid = tess.Triangulate(pgPoints, pgPointCount, pgEdges, pgEdgeCount);

            valid = valid && tess.ApplyDelaunay(pgPoints, pgEdges);
            if (valid)
            {
                // Output.
                NativeArray <int3> cells = tess.RemoveExterior(ref triCount);
                for (var i = 0; i < triCount; ++i)
                {
                    var a = (UInt16)cells[i].x;
                    var b = (UInt16)cells[i].y;
                    var c = (UInt16)cells[i].z;
                    if (a != b && b != c && a != c)
                    {
                        outputIndices[indexCount++] = a;
                        outputIndices[indexCount++] = c;
                        outputIndices[indexCount++] = b;
                    }
                    maxCount = math.max(math.max(math.max(cells[i].x, cells[i].y), cells[i].z), maxCount);
                }
                maxCount = (maxCount != 0) ? (maxCount + 1) : 0;
                for (var i = 0; i < maxCount; ++i)
                {
                    outputVertices[vertexCount++] = pgPoints[i];
                }
                cells.Dispose();
            }

            tess.Cleanup();
            return(valid);
        }
Пример #4
0
        internal static bool Condition(Allocator allocator, float factorArea, float targetArea, ref NativeArray <float2> pgPoints, ref int pgPointCount, ref NativeArray <int2> pgEdges, ref int pgEdgeCount, ref NativeArray <float2> vertices, ref int vertexCount, ref NativeArray <int> indices, ref int indexCount, ref float maxArea)
        {
            // Process Triangles.
            maxArea = 0.0f;
            var refined    = false;
            var validGraph = true;

            // Temporary Stuffs.
            int triangleCount = 0, invalidTriangle = -1, inputPointCount = pgPointCount;
            var encroach  = new NativeArray <UEncroachingSegment>(UTess.kMaxEdgeCount, allocator);
            var triangles = new NativeArray <UTriangle>(UTess.kMaxTriangleCount, allocator);

            UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea);
            factorArea = factorArea != 0 ? math.clamp(factorArea, kMinAreaFactor, kMaxAreaFactor) : factorArea;
            var criArea = maxArea * factorArea;

            criArea = math.max(criArea, targetArea);

            // Refine
            while (!refined && validGraph)
            {
                // Check if any of the Triangle is Invalid or Segment is invalid. If yes, Refine.
                for (int i = 0; i < triangleCount; ++i)
                {
                    if (RequiresRefining(triangles[i], criArea))
                    {
                        invalidTriangle = i;
                        break;
                    }
                }

                // Find any Segment that can be Split based on the Input Length.
                // todo.

                if (invalidTriangle != -1)
                {
                    // Get all Segments that are encroached.
                    var t             = triangles[invalidTriangle];
                    var encroachCount = 0;
                    FetchEncroachedSegments(pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref encroach, ref encroachCount, t.c);

                    // Split each Encroached Segments. If no segments are encroached. Split the Triangle.
                    if (encroachCount != 0)
                    {
                        for (int i = 0; i < encroachCount; ++i)
                        {
                            SplitSegments(ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, encroach[i]);
                        }
                    }
                    else
                    {
                        // Update Triangulation.
                        var split = t.c.center;
                        pgPoints[pgPointCount++] = split;
                    }

                    // Tessellate again.
                    indexCount = 0; vertexCount = 0;
                    validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref vertices, ref vertexCount, ref indices, ref indexCount);

                    // Build Internal Triangles.
                    encroachCount = 0; triangleCount = 0; invalidTriangle = -1;
                    if (validGraph)
                    {
                        UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea);
                    }

                    // More than enough Steiner points inserted. This handles all sort of weird input sprites very well (even random point cloud).
                    if (pgPointCount - inputPointCount > kMaxSteinerCount)
                    {
                        break;
                    }
                }
                else
                {
                    refined = true;
                }
            }

            // Dispose off
            triangles.Dispose();
            encroach.Dispose();
            return(refined);
        }
Пример #5
0
        public static float4 Subdivide(Allocator allocator, NativeArray <float2> points, NativeArray <int2> edges, ref NativeArray <float2> outVertices, ref int outVertexCount, ref NativeArray <int> outIndices, ref int outIndexCount, ref NativeArray <int2> outEdges, ref int outEdgeCount, float areaFactor, float targetArea, int refineIterations, int smoothenIterations)
        {
            // Inputs are garbage, just early out.
            float4 ret = float4.zero;

            outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0;
            if (points.Length < 3 || points.Length >= kMaxVertexCount || 0 == edges.Length)
            {
                return(ret);
            }

            // Do a proper Delaunay Triangulation.
            int tsIndexCount = 0, tsVertexCount = 0;
            NativeArray <int>    tsIndices  = new NativeArray <int>(kMaxIndexCount, allocator);
            NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator);
            var validGraph = Tessellator.Tessellate(allocator, points, points.Length, edges, edges.Length, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount);

            // Refinement and Smoothing.
            bool refined            = false;
            bool refinementRequired = (targetArea != 0 || areaFactor != 0);

            if (validGraph && refinementRequired)
            {
                // Do Refinement until success.
                float maxArea = 0;
                float incArea = 0;
                int   rfEdgeCount = 0, rfPointCount = 0, rfIndexCount = 0, rfVertexCount = 0;
                NativeArray <int2>   rfEdges    = new NativeArray <int2>(kMaxEdgeCount, allocator);
                NativeArray <float2> rfPoints   = new NativeArray <float2>(kMaxVertexCount, allocator);
                NativeArray <int>    rfIndices  = new NativeArray <int>(kMaxIndexCount, allocator);
                NativeArray <float2> rfVertices = new NativeArray <float2>(kMaxVertexCount, allocator);
                ret.x            = 0;
                refineIterations = Math.Min(refineIterations, kMaxRefineIterations);

                if (targetArea != 0)
                {
                    // Increment for Iterations.
                    incArea = (targetArea / 10);

                    while (targetArea < kMaxArea && refineIterations > 0)
                    {
                        // Do Mesh Refinement.
                        CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount);
                        CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount);
                        refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea);

                        if (refined && rfIndexCount > rfPointCount)
                        {
                            // Copy Out
                            ret.x = areaFactor;
                            TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                            break;
                        }

                        refined    = false;
                        targetArea = targetArea + incArea;
                        refineIterations--;
                    }
                }
                else if (areaFactor != 0)
                {
                    // Increment for Iterations.
                    areaFactor = math.lerp(0.1f, 0.54f, (areaFactor - 0.05f) / 0.45f); // Specific to Animation.
                    incArea    = (areaFactor / 10);

                    while (areaFactor < 0.8f && refineIterations > 0)
                    {
                        // Do Mesh Refinement.
                        CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount);
                        CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount);
                        refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea);

                        if (refined && rfIndexCount > rfPointCount)
                        {
                            // Copy Out
                            ret.x = areaFactor;
                            TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                            break;
                        }

                        refined    = false;
                        areaFactor = areaFactor + incArea;
                        refineIterations--;
                    }
                }

                if (refined)
                {
                    // Sanitize generated geometry data.
                    var preSmoothen = outVertexCount;
                    if (ret.x != 0)
                    {
                        VertexCleanupConditioner(tsVertexCount, ref rfIndices, ref rfIndexCount, ref rfVertices, ref rfVertexCount);
                    }

                    // Smoothen. At this point only vertex relocation is allowed, not vertex addition/removal.
                    // Note: Only refined mesh contains Steiner points and we only smoothen these points.
                    ret.y = 0;
                    smoothenIterations = math.clamp(smoothenIterations, 0, kMaxSmoothenIterations);
                    while (smoothenIterations > 0)
                    {
                        var smoothen = Smoothen.Condition(allocator, ref rfPoints, rfPointCount, rfEdges, rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount);
                        if (!smoothen)
                        {
                            break;
                        }
                        // Copy Out
                        ret.y = (float)(smoothenIterations);
                        TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                        smoothenIterations--;
                    }

                    // Sanitize generated geometry data.
                    var postSmoothen = outVertexCount;
                    if (ret.y != 0)
                    {
                        VertexCleanupConditioner(tsVertexCount, ref outIndices, ref outIndexCount, ref outVertices, ref outVertexCount);
                    }
                }

                rfVertices.Dispose();
                rfIndices.Dispose();
                rfPoints.Dispose();
                rfEdges.Dispose();
            }

            // Refinement failed but Graph succeeded.
            if (validGraph && !refined)
            {
                // Copy Out
                TransferOutput(edges, edges.Length, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount);
            }

            // Dispose Temp Memory.
            tsVertices.Dispose();
            tsIndices.Dispose();
            return(ret);
        }
Пример #6
0
        public static float4 Tessellate(Allocator allocator, NativeArray <float2> points, NativeArray <int2> edges, ref NativeArray <float2> outVertices, ref int outVertexCount, ref NativeArray <int> outIndices, ref int outIndexCount, ref NativeArray <int2> outEdges, ref int outEdgeCount)
        {
            // Inputs are garbage, just early out.
            float4 ret = float4.zero;

            outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0;
            if (points.Length < 3 || points.Length >= kMaxVertexCount)
            {
                return(ret);
            }

            // Ensure inputs form a proper PlanarGraph.
            bool validGraph = false, handleEdgeCase = false;
            int  pgEdgeCount = 0, pgPointCount = 0;
            NativeArray <int2>   pgEdges  = new NativeArray <int2>(kMaxEdgeCount, allocator);
            NativeArray <float2> pgPoints = new NativeArray <float2>(kMaxVertexCount, allocator);

            // Valid Edges and Paths, correct the Planar Graph. If invalid create a simple convex hull rect.
            if (0 != edges.Length)
            {
                validGraph = PlanarGraph.Validate(allocator, points, points.Length, edges, edges.Length, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount);
            }


// Fallbacks are now handled by the Higher level packages. Enable if UTess needs to handle it.
// #if UTESS_QUAD_FALLBACK
//             if (!validGraph)
//             {
//                 pgPointCount = 0;
//                 handleEdgeCase = true;
//                 ModuleHandle.Copy(points, pgPoints, points.Length);
//                 GraphConditioner(points, ref pgPoints, ref pgPointCount, ref pgEdges, ref pgEdgeCount, false);
//             }
// #else

// If its not a valid Graph simply return back input Data without triangulation instead of going through UTess (pointless wasted cpu cycles).
            if (!validGraph)
            {
                outEdgeCount   = edges.Length;
                outVertexCount = points.Length;
                ModuleHandle.Copy(edges, outEdges, edges.Length);
                ModuleHandle.Copy(points, outVertices, points.Length);
            }

            // Do a proper Delaunay Triangulation if Inputs are valid.
            if (pgPointCount > 2 && pgEdgeCount > 2)
            {
                int tsIndexCount = 0, tsVertexCount = 0;
                NativeArray <int>    tsIndices = new NativeArray <int>(kMaxIndexCount, allocator);
                NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator);
                validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount);
                if (validGraph)
                {
                    // Copy Out
                    TransferOutput(pgEdges, pgEdgeCount, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount);
                    if (handleEdgeCase == true)
                    {
                        outEdgeCount = 0;
                    }
                }
                tsVertices.Dispose();
                tsIndices.Dispose();
            }

            // Dispose Temp Memory.
            pgPoints.Dispose();
            pgEdges.Dispose();
            return(ret);
        }
Пример #7
0
        // Perform Voronoi based Smoothing. Does not add/remove points but merely relocates internal vertices so they are uniform distributed.
        public static bool Condition(Allocator allocator, ref NativeArray <float2> pgPoints, int pgPointCount, NativeArray <int2> pgEdges, int pgEdgeCount, ref NativeArray <float2> vertices, ref int vertexCount, ref NativeArray <int> indices, ref int indexCount)
        {
            // Build Triangles and Edges.
            float maxArea = 0, cmpArea = 0;
            bool  polygonCentroid = true, validGraph = true;
            int   triangleCount = 0, delaEdgeCount = 0, affectingEdgeCount = 0;
            var   triangles    = new NativeArray <UTriangle>(indexCount / 3, allocator);
            var   delaEdges    = new NativeArray <int4>(indexCount, allocator);
            var   voronoiEdges = new NativeArray <int4>(indexCount, allocator);
            var   connectedTri = new NativeArray <int4>(vertexCount, allocator);
            var   voronoiCheck = new NativeArray <int>(indexCount, allocator);
            var   affectsEdges = new NativeArray <int>(indexCount, allocator);
            var   triCentroids = new NativeArray <int>(vertexCount, allocator);

            UTess.BuildTrianglesAndEdges(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref delaEdges, ref delaEdgeCount, ref maxArea);
            var refinedEdges = new NativeArray <int4>(delaEdgeCount, allocator);

            // Sort the Delaunay Edges.
            unsafe
            {
                UTess.InsertionSort <int4, DelaEdgeCompare>(
                    NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(delaEdges), 0, delaEdgeCount - 1,
                    new DelaEdgeCompare());
            }

            // TrimEdges. Update Triangle Info for Shared Edges and remove Duplicates.
            RefineEdges(ref refinedEdges, ref delaEdges, ref delaEdgeCount, ref voronoiEdges);

            // Now for each point, generate Voronoi diagram.
            for (int i = 0; i < vertexCount; ++i)
            {
                // Try moving this to Centroid of the Voronoi Polygon.
                GetAffectingEdges(i, delaEdges, delaEdgeCount, ref affectsEdges, ref voronoiCheck, ref affectingEdgeCount);
                var bounded = affectingEdgeCount != 0;

                // Check for Boundedness
                for (int j = 0; j < affectingEdgeCount; ++j)
                {
                    // Edge Index.
                    var ei = affectsEdges[j];
                    if (delaEdges[ei].z == -1 || delaEdges[ei].w == -1)
                    {
                        bounded = false;
                        break;
                    }
                }

                // If this is bounded point, relocate to Voronoi Diagram's Centroid
                if (bounded)
                {
                    polygonCentroid = ConnectTriangles(ref connectedTri, ref affectsEdges, ref voronoiCheck, voronoiEdges, affectingEdgeCount);
                    if (!polygonCentroid)
                    {
                        break;
                    }

                    float2 point = float2.zero;
                    float  area = 0, distance = 0;
                    for (int k = 0; k < affectingEdgeCount; ++k)
                    {
                        CentroidByPolygon(connectedTri[k], triangles, ref point, ref area, ref distance);
                    }
                    point      /= (3 * area);
                    pgPoints[i] = point;
                }
            }

            // Do Delaunay Again.
            int srcIndexCount = indexCount, srcVertexCount = vertexCount;

            indexCount = 0; vertexCount = 0; triangleCount = 0;
            if (polygonCentroid)
            {
                validGraph = Tessellator.Tessellate(allocator, pgPoints, pgPointCount, pgEdges, pgEdgeCount, ref vertices, ref vertexCount, ref indices, ref indexCount);
                if (validGraph)
                {
                    UTess.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref cmpArea);
                }
            }

            // Cleanup.
            triangles.Dispose();
            delaEdges.Dispose();
            refinedEdges.Dispose();
            voronoiCheck.Dispose();
            voronoiEdges.Dispose();
            affectsEdges.Dispose();
            triCentroids.Dispose();
            connectedTri.Dispose();
            return(validGraph && srcIndexCount == indexCount && srcVertexCount == vertexCount && (cmpArea < maxArea * kAreaTolerance));
        }
Пример #8
0
        public static float4 Subdivide(Allocator allocator, NativeArray <float2> points, NativeArray <int2> edges, ref NativeArray <float2> outVertices, ref int outVertexCount, ref NativeArray <int> outIndices, ref int outIndexCount, ref NativeArray <int2> outEdges, ref int outEdgeCount, float areaFactor, float targetArea, int smoothenCount)
        {
            // Inputs are garbage, just early out.
            outEdgeCount = 0; outIndexCount = 0; outVertexCount = 0;
            if (points.Length == 0)
            {
                return(float4.zero);
            }

            // Do a proper Delaunay Triangulation.
            float4               ret = float4.zero;
            int                  tsIndexCount = 0, tsVertexCount = 0;
            NativeArray <int>    tsIndices  = new NativeArray <int>(kMaxIndexCount, allocator);
            NativeArray <float2> tsVertices = new NativeArray <float2>(kMaxVertexCount, allocator);
            var                  validGraph = Tessellator.Tessellate(allocator, points, points.Length, edges, edges.Length, ref tsVertices, ref tsVertexCount, ref tsIndices, ref tsIndexCount);

            // Refinement and Smoothing.
            bool refined            = false;
            bool refinementRequired = (targetArea != 0 || areaFactor != 0);

            if (validGraph && refinementRequired)
            {
                // Do Refinement until success.
                float maxArea = 0;
                int   rfEdgeCount = 0, rfPointCount = 0, rfIndexCount = 0, rfVertexCount = 0;
                NativeArray <int2>   rfEdges    = new NativeArray <int2>(kMaxEdgeCount, allocator);
                NativeArray <float2> rfPoints   = new NativeArray <float2>(kMaxVertexCount, allocator);
                NativeArray <int>    rfIndices  = new NativeArray <int>(kMaxIndexCount, allocator);
                NativeArray <float2> rfVertices = new NativeArray <float2>(kMaxVertexCount, allocator);

                if (targetArea != 0)
                {
                    while (targetArea < kMaxArea)
                    {
                        // Do Mesh Refinement.
                        CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount);
                        CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount);
                        refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea);

                        if (refined && rfIndexCount > rfPointCount)
                        {
                            // Copy Out
                            ret.x = areaFactor;
                            TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                            break;
                        }

                        refined    = false;
                        targetArea = targetArea * 2.0f;
                    }
                }
                else if (areaFactor != 0)
                {
                    while (areaFactor < 0.8f)
                    {
                        // Do Mesh Refinement.
                        CopyGraph(points, points.Length, ref rfPoints, ref rfPointCount, edges, edges.Length, ref rfEdges, ref rfEdgeCount);
                        CopyGeometry(tsIndices, tsIndexCount, ref rfIndices, ref rfIndexCount, tsVertices, tsVertexCount, ref rfVertices, ref rfVertexCount);
                        refined = Refinery.Condition(allocator, areaFactor, targetArea, ref rfPoints, ref rfPointCount, ref rfEdges, ref rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount, ref maxArea);

                        if (refined && rfIndexCount > rfPointCount)
                        {
                            // Copy Out
                            ret.x = areaFactor;
                            TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                            break;
                        }

                        refined    = false;
                        areaFactor = areaFactor * 2.0f;
                    }
                }

                if (refined)
                {
                    // Smoothen. At this point only vertex relocation is allowed, not vertex addition/removal.
                    // Note: Only refined mesh contains Steiner points and we only smoothen these points.
                    smoothenCount = math.clamp(smoothenCount, 0, kMaxSmoothenIterations);
                    while (smoothenCount > 0)
                    {
                        var smoothen = Smoothen.Condition(allocator, ref rfPoints, rfPointCount, rfEdges, rfEdgeCount, ref rfVertices, ref rfVertexCount, ref rfIndices, ref rfIndexCount);
                        if (!smoothen)
                        {
                            break;
                        }
                        // Copy Out
                        ret.y = (float)(smoothenCount);
                        TransferOutput(rfEdges, rfEdgeCount, ref outEdges, ref outEdgeCount, rfIndices, rfIndexCount, ref outIndices, ref outIndexCount, rfVertices, rfVertexCount, ref outVertices, ref outVertexCount);
                        smoothenCount--;
                    }
                }

                rfVertices.Dispose();
                rfIndices.Dispose();
                rfPoints.Dispose();
                rfEdges.Dispose();
            }

            // Refinement failed but Graph succeeded.
            if (validGraph && !refined)
            {
                // Copy Out
                TransferOutput(edges, edges.Length, ref outEdges, ref outEdgeCount, tsIndices, tsIndexCount, ref outIndices, ref outIndexCount, tsVertices, tsVertexCount, ref outVertices, ref outVertexCount);
            }

            // Dispose Temp Memory.
            tsVertices.Dispose();
            tsIndices.Dispose();
            return(ret);
        }