// Perform Voronoi based Smoothing. Does not add/remove points but merely relocates internal vertices so they are uniform distributed. internal 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, cmxArea = 0, minArea = 0, cmnArea = 0, avgArea = 0, minEdge = 0, maxEdge = 0, avgEdge = 0; bool polygonCentroid = true, validGraph = true; int triangleCount = 0, delaEdgeCount = 0, affectingEdgeCount = 0; var triangles = new NativeArray <UTriangle>(indexCount, allocator); // Intentionally added more room than actual Triangles needed here. 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); ModuleHandle.BuildTrianglesAndEdges(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref delaEdges, ref delaEdgeCount, ref maxArea, ref avgArea, ref minArea); var refinedEdges = new NativeArray <int4>(delaEdgeCount, allocator); // Sort the Delaunay Edges. unsafe { ModuleHandle.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) { ModuleHandle.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref cmxArea, ref avgArea, ref cmnArea, ref maxEdge, ref avgEdge, ref minEdge); } // This Edge validation prevents artifacts by forcing a fallback. todo: Fix the actual bug in Outline generation. validGraph = validGraph && (cmxArea < maxArea * kMaxAreaTolerance) && (maxEdge < avgEdge * kMaxEdgeTolerance); } // Cleanup. triangles.Dispose(); delaEdges.Dispose(); refinedEdges.Dispose(); voronoiCheck.Dispose(); voronoiEdges.Dispose(); affectsEdges.Dispose(); triCentroids.Dispose(); connectedTri.Dispose(); return(validGraph && srcIndexCount == indexCount && srcVertexCount == vertexCount); }
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 minArea = 0.0f; var avgArea = 0.0f; var refined = false; var validGraph = true; // Temporary Stuffs. int triangleCount = 0, invalidTriangle = -1, inputPointCount = pgPointCount; var encroach = new NativeArray <UEncroachingSegment>(ModuleHandle.kMaxEdgeCount, allocator); var triangles = new NativeArray <UTriangle>(ModuleHandle.kMaxTriangleCount, allocator); ModuleHandle.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea, ref avgArea, ref minArea); 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) { ModuleHandle.BuildTriangles(vertices, vertexCount, indices, indexCount, ref triangles, ref triangleCount, ref maxArea, ref avgArea, ref minArea); } // 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); }