public void AddEdge(FloodfillPointBucket from, FloodfillPointBucket to) { if (!edges.ContainsKey(from)) { edges.Add(from, new List <FloodfillPointBucket>()); } if (!edges[from].Contains(to)) { edges[from].Add(to); } }
//no better idea for this right now /* * Problem is: I need to have seperate queues, because candidates may be in enclosed spaces which should get discarded * However, candidates may also be in the same open space and need to get merged * -> open and closed spaces can never have their queues mixed * */ internal void MergeWith(FloodfillPointBucket bucketB, Dictionary <Vector2Int, FloodfillPointBucket> pointToBucket) { Vector2Int newOg = new Vector2Int(Math.Min(this.ogPos.x, bucketB.ogPos.x), Math.Min(this.ogPos.y, bucketB.ogPos.y)); //transfering queues is not necessary with new variant of global visited set //while (bucketB.nextQueue.Count > 0) //{ // Vector2Int transferCandidate = bucketB.nextQueue.Dequeue(); // if (!visitedPoints.Contains(transferCandidate)) // nextQueue.Enqueue(transferCandidate); //} ogPos = newOg; }
private bool TickBucketFloodfillV3( FloodfillPointBucket bucket, HashSet <Vector2Int> visited, HashSet <FloodfillPointBucket> buckets, BucketGraph graph, List <FloodfillDebugInfo> debugInfos) { if (bucket.nextQueue.Count == 0) { UnityEngine.Debug.Log("break"); return(false); } Vector2Int pixel = bucket.nextQueue.Dequeue(); List <Vector2Int> mergeCandidates = new List <Vector2Int>(); FloodfillStepNotBlack( pixel.x, pixel.y, bucket.ogPos.x, bucket.ogPos.y, l0, l1, visited, bucket.visitedPoints, bucket.nextQueue, ref mergeCandidates); foreach (Vector2Int mergeCandidate in mergeCandidates) { foreach (FloodfillPointBucket retrieveBucket in buckets) { if (retrieveBucket.visitedPoints.Contains(mergeCandidate)) { graph.AddEdge(bucket, retrieveBucket); graph.AddEdge(retrieveBucket, bucket); } } } return(true); }
/* Routine to find top left point * * Pipeline: * - Floodfill image from top left with black to find borders * - Find candidate points * - Floodfill from candidate points * - Select point where floodfill lasts the longest. * * -> For now shelved because not performant enough * -> Should be optimizable with bucket based approach * */ internal void FindTopLeftPointByFloodfill() { HashSet <Vector2Int> candidates = new HashSet <Vector2Int>(); //Step 1 CVFloodfill(7); /* Step 2 * Get all candidate pixels where layout is: * b = black pixel * C = candidate * x = non-black pixel * * b b b b * b b c x * b b x x * * */ for (int y = 0; y < l1; y++) { for (int x = 0; x < l0; x++) { //if (x > 1000 || y > 1000) // continue; int intensity = GetIntensity(x, y); //if pixel is exactly black (was set by first floodfill) if (intensity == INTENSITY_BLACK) { TryAddCandidateForTopLeftPoint(candidates, x, y); } } } UnityEngine.Debug.Log("Found " + candidates.Count + " candidates"); //Dictionary<Vector2Int, FloodfillPointBucket>pointToBucket = new Dictionary<Vector2Int, FloodfillPointBucket>(); //foreach (Vector2Int candidate in candidates) //{ // pointToBucket.Add(candidate, new FloodfillPointBucket(candidate)); //} //Step 3 - Find top left pixel of map from candidates /* Idea: Start floodfill from each candidate for non black pixels (only in bottom right direction) * Origin pixel which had the largest number of pixels floodfilled must be top left * * Problem: Still ~9000 candidates in test map (because of jagged outlines). * Finishing 9000 floodfills many of which are traversing a big part of the map takes too long * * Idea: When two floodfills meet -> merge buckets to guarantee each pixel only gets processed once (except for merge pixels) * * Problem: Merging needs to iterate all pixels anyway. * * Idea: Make buckets outer loop!! This way all buckets must be closed by the point of a merge * * * */ int safety = 0; //old version -> buckets are inner loop //while (candidates.Count > 1) //{ // HashSet<Vector2Int> toRemove = new HashSet<Vector2Int>(); // foreach (Vector2Int candidate in candidates) // { // safety++; // if (pointToBucket.ContainsKey(candidate)) // { // if (!TickBucketFloodfill(toRemove, pointToBucket[candidate], pointToBucket)) // ; // } // //problem pointToBucket got removed but candidate didn't get removed yet. // } // UnityEngine.Debug.Log(candidates.Count); // foreach (Vector2Int remove in toRemove) // candidates.Remove(remove); // //weird! still 3000 buckets after 15m pixels have been iterated... should have converged since picture is only ~15m pixels in total! // //-> because pixels are associated with original bucket even after merge happened // if (safety > 15000000) // { // UnityEngine.Debug.Log(candidates.Count); // break; // } //} //HashSet<Vector2Int> visited = new HashSet<Vector2Int>(); ////new version -> buckets are outer loop //foreach (Vector2Int vec in candidates) //{ // //each candidate gets traversed but if a pixel is already occupied for this candidate the nextQueue of that bucket will be 0 // /* // * The only case where buckets need to be merged is when one bucket is farther left or to the top than another bucket since // * otherwise the origin pixel of the bucket would be inside the area traversed by the first bucket. // * // * Problem with v2 -> migrating queue to bucket is costly. Migrating closed list even more so. // * // * */ // while (TickBucketFloodfillV2(vec, pointToBucket, visited)) // { // safety++; // if (safety > 20000000) // break; // } // if (safety > 20000000) // break; //} //new solution: only create bucket if we have to. And create graph of buckets. //In the end traverse graph to find top left pixel //still too slow! Nvm this stuff. let the user do it. HashSet <Vector2Int> visited = new HashSet <Vector2Int>(); BucketGraph graph = new BucketGraph(); HashSet <FloodfillPointBucket> buckets = new HashSet <FloodfillPointBucket>(); List <FloodfillDebugInfo> debugInfos = new List <FloodfillDebugInfo>(); int smax = 10000000; foreach (Vector2Int v2i in candidates) { if (visited.Contains(v2i)) { continue; } FloodfillPointBucket currentBucket = new FloodfillPointBucket(v2i); buckets.Add(currentBucket); while (TickBucketFloodfillV3(currentBucket, visited, buckets, graph, debugInfos)) { safety++; if (safety > smax) { break; } } if (safety >= smax) { UnityEngine.Debug.Log("too many steps!"); break; } } UnityEngine.Debug.Log("number of edges: " + graph.edges.Count); UnityEngine.Debug.Log("number of visited pixels: " + visited.Count); foreach (FloodfillDebugInfo debugInfo in debugInfos) { UnityEngine.Debug.Log(debugInfo.ToString()); } }
/// <summary> /// merges two buckets based on which buckets origin pixel is closer to the top right /// </summary> /// <param name="bucketA"></param> /// <param name="bucketB"></param> /// <returns>The obsolete bucket index (initial position of the bucket which got merged)</returns> private Vector2Int MergeBuckets(FloodfillPointBucket bucketA, FloodfillPointBucket bucketB, Dictionary <Vector2Int, FloodfillPointBucket> pointToBucket) { bucketA.MergeWith(bucketB, pointToBucket); return(bucketB.initialPos); }