Finding "Centroids" On A Grid - Slow Performance

This is something I already have a working solution for, but it takes forever (sometimes >5 hours, and I’ve done this half a dozen times in the last week fixing minor details) so I’m curious if there are suggestions for improvement.

I have a grid of cells, like the image below:
7002047--827597--SavedScreen.png
That’s 180 x 270, or 48600 total cells, and the colors have the following meaning:

Grey - walkable
Yellow - walkable and interactive
Black - wall or an interactive object (so unwalkable)

One can see that there are regions of the grey cells enclosed by walls. These are “Rooms.” For various reasons I’m interested in getting what I’m calling (for lack of a better word) the “centroids” of each room: the minimum number (and location) of points in the room required to see every point in the room. Imagine a patrolling robot for example, who wants to check every point.

My current code to do this is below:

public void SetCentroids(CellWalkableState[,] grid)
    {
        centroids = new List<((int, int), int)>();
        List<(int, int)> cellsCopy = new List<(int, int)>();
        for (int i = 0; i < cells.Count; i++)
        {
            cellsCopy.Add(cells[i]);
        }
        var perCellInView = cellsCopy.AsParallel().Select(x => (x, StaticClass.FindInView(x, grid)));
        var perCellInViewOrdered = perCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count).ToList();
        //Debug.Log("counts: " + perCellInViewOrdered[0].Item2.Count + "," + perCellInViewOrdered[1].Item2.Count);
        List<(int, int)> roomCellsAdded = new List<(int, int)>();
        while(roomCellsAdded.Count < cells.Count)
        {
            if(cellsCopy.Count == 0)
            {
                Debug.LogError("something is wrong here.");
            }
            var centroid = perCellInViewOrdered[0].x;
            var centroidCells = perCellInViewOrdered[0].Item2;
            if(centroidCells.Count == 0)
            {
                Debug.Log("this shouldnt be happening");
                break;
            }
            cellsCopy.Remove(centroid);
            roomCellsAdded.AddRange(centroidCells);
            centroids.Add((centroid, centroidCells.Count));
            var loopPerCellInView = perCellInView.AsParallel().Where(x => centroids.Select(y => y.Item1).Contains(x.x) == false).Select(x => (x.x, x.Item2.Except(roomCellsAdded).ToList()));
            perCellInViewOrdered = loopPerCellInView.AsParallel().OrderByDescending(xxx => xxx.Item2.Count()).ToList();
            //Debug.Log("counts: " + perCellInViewOrdered[0].Item2.Count + "," + perCellInViewOrdered[1].Item2.Count);
        }
    }

“cells” is just a List<(int,int)> in the Room class which contains all of the grey squares for a given room. The “StaticClass.FindInView” method finds every location that a given point can see (I can provide that method if desired). The general flow is:

–For each point in a room, find what other points it can view (this will be a list/IEnumerable of “((int,int),List<(int,int)>)” structs)
–Sort the list by the count of the second item, such that the location with the most points-it-can-view is first
-Loop through the following:
–Store the first location as a centroid, as well as the points it can view
–get a “derivation” of the original list that does not include already-stored centroids, and does not include already-in-view locations in the sub-list for each centroid
–sort the “derived” list by the count of the second item

As I mentioned before, this is taking literally hours every time. Makes some sense since I’m iterating through groups of sometimes 10,000+ entries in a room, modifying them and sorting them each loop, but it still seems too long.

In addition, the number of “centroids” I’m seeing seems high - for one room on that grid it was 37 in total. It should be, like, 4 or 5 for most of those rooms.

Does anyone see anything I might be doing wrong or inefficiently?

Don’t you just want to floodfill the walkable areas and see how many unique walkable areas there are?

Starting from upper left pixel, find the first walkable pixel, floodfill it with a “area 0” token, replacing the walkable pixels.

Then scan ahead until you find the next walkable pixel, fill that with “area 1” token, replacing the walkable pixels

etc until you’re done. As you floodfill you could add them to another list, or just use them in-place.

I don’t think so, from what I can tell from the Wikipedia page. “points in view” is an explicit requirement., found by rotating 360 degrees from a given point and checking as far as one can go in a direction. None of the examples on that page look like they’re doing that, but maybe I’m missing something.

Also, it’s possible for a subsequent “centroid” to be in the already-in-view list, so it’s not just completely separate regions. Using the first example on the Wikipedia page, there’s no point in that center there that can see ALL other points in the same “room.” The far corners won’t be visible. However, a second centroid that can see those far corners would be one of the points already in view of the first centroid.

On you’re doing LOS (line of sight) based on the above? I thought you were just interested in “can I get to this or not.”

I’m not sure what the general LOS solution would be but I guess raycasting would be a start.

But then developing “The best” set of points for the robot to patrol from is a FAR harder problem, akin to the traveling salesman problem of graph optimization.

https://en.wikipedia.org/wiki/Travelling_salesman_problem

Probably just want to author some extra patrol points in there by hand so you can move on with things.

1 Like

okay, thanks for the help.