Creating graph from polygons takes too long with large number of vertices - c#

I am trying to build a graph out of multipolygons. The code works fine when I don't have large amount of vertices but with 1M it is not satisfying. Any performance improvement suggestions?
public Graph(List<Polygon> PolygonsSet)
{
edges = new List<Edge>();
graphID = Guid.NewGuid();
//Setting up Graph instance by adding vertices, edges and polygons
foreach (Polygon Polygon in PolygonsSet)
{
List<Vertex> vertices = Polygon.vertices;
// Clear pre-existing edges in the case this is an updating process.
Polygon.edges.Clear();
//If there is only one polygon, treat it as boundary
if (PolygonsSet.Count() == 1)
{
Polygon.isBoundary = true;
}
//If first and last point of vertices list are the same, remove last.
if (vertices.First().Equals(vertices.Last()) && vertices.Count() > 1)
{
vertices = vertices.Take(vertices.Count() - 1).ToList();
}
//For each point, creates vertex and associated edge and adds them
//to the polygons Dictionary
int vertexCount = vertices.Count();
// If valid polygon
if (vertexCount >= 3)
{
int newId = GetNextId();
for (var j = 0; j < vertexCount; j++)
{
int next_index = (j + 1) % vertexCount;
Vertex vertex = vertices[j];
Vertex next_vertex = vertices[next_index];
Edge edge = new Edge(vertex, next_vertex);
//If is a valid polygon, add id to vertex and
//edge to vertices dictionary
if (vertexCount > 2)
{
vertex.polygonId = newId;
next_vertex.polygonId = newId;
Polygon gPol = new Polygon();
if (polygons.TryGetValue(newId, out gPol))
{
gPol.edges.Add(edge);
}
else
{
Polygon.edges.Add(edge);
Polygon.id = newId;
polygons.Add(newId, Polygon);
}
}
AddEdge(edge);
}
}
}
}
and AddEdge method is;
public void AddEdge(Edge edge)
{
List<Edge> startEdgesList = new List<Edge>();
List<Edge> endEdgesList = new List<Edge>();
if (graph.TryGetValue(edge.StartVertex, out startEdgesList))
{
if (!startEdgesList.Contains(edge)) { startEdgesList.Add(edge); }
}
else
{
graph.Add(edge.StartVertex, new List<Edge>() { edge });
}
if (graph.TryGetValue(edge.EndVertex, out endEdgesList))
{
if (!endEdgesList.Contains(edge)) { endEdgesList.Add(edge); }
}
else
{
graph.Add(edge.EndVertex, new List<Edge>() { edge });
}
if (!edges.Contains(edge)) { edges.Add(edge); }
}
Code works fine, my only concern is performance.
I tried to simplify the polygons and even used convex hull to reduce the workload but in some cases I need to use the polygon as it is.
So any help will be appreciated...

In the code lines starting with if (vertexCount > 2), you test the vertext count, but the count did not change since the last test if (vertexCount >= 3). Drop this second if.
Then you create a new polygon with Polygon gPol = new Polygon();. This polygon gets replaced immediately afterwards by the out parameter in polygons.TryGetValue(newId, out gPol).
Either TryGetValue yields true, then gPol become the polygon found in the collection, or TryGetValue yields false and gPol becomes null. Do not assign gPol
Polygon gPol;
if (polygons.TryGetValue(newId, out gPol)) ...
Or use the C# 7.0 syntax
if (polygons.TryGetValue(newId, out Polygon gPol)) ...
In the else case, you should create a new polygon (because gPol is null). However, you can simplify this code, because the edge is added in both cases:
if (!polygons.TryGetValue(newId, out Polygon gPol)) {
gPol = new Polygon { id = newId };
polygons.Add(newId, gPol);
}
gPol.edges.Add(edge);
You also seem to confuse Polygon with gPol.
Since newId gets created before the for-loop, you can move the code finding or creating the polyon out of the loop
int vertexCount = vertices.Count();
if (vertexCount >= 3)
{
int newId = GetNextId();
if (!polygons.TryGetValue(newId, out Polygon gPol)) {
gPol = new Polygon { id = newId };
polygons.Add(newId, gPol);
}
for (var j = 0; j < vertexCount; j++)
{
int next_index = (j + 1) % vertexCount;
Vertex vertex = vertices[j];
Vertex next_vertex = vertices[next_index];
Edge edge = new Edge(vertex, next_vertex);
vertex.polygonId = newId;
next_vertex.polygonId = newId;
gPol.edges.Add(edge);
AddEdge(edge);
}
}
In AddEdge you are repeating the same error of overwriting the lists just created by TryGetValue.

Related

Spawning pathfinder neighbours

I am trying to spawn the neighbours of my path finder but when doing so the original path (path) blue also becomes covered up. I have tried switching of the order of spawn but this does not seem to help the problem. Quite new to Unity and would appreciate any insight into how I might go about spawning the path the neighbours without losing the original blue path.
Obtaining path neighbours; spawning the path + neighbours
for (int i = 1; i < path.Count; i++)
{
var pathCellPosition = path[i];
pathCellPosition.Position = new Vector3(pathCellPosition.Position.x, pathPrefab.transform.position.y, pathCellPosition.Position.z);
List<Node> neighbours = GetNeighbours(pathCellPosition);
var p = Instantiate(pathPrefab, pathCellPosition.Position, Quaternion.identity, PathCells);
foreach (Node n in neighbours)
{
neighbourPrefab.GetComponentInChildren<Text>().text = n.Cost.ToString();
Instantiate(neighbourPrefab, n.Position, Quaternion.identity, p.transform);
}
}
Original path
Path getting covered by neighbours
Implemented suggestions fixing part of the problem
The tiles around now seem to appear but when applying the same solution to implement neighbours of neighbours it seems to cause an overlap with the original neighbouring nodes.
Code changes:
HashSet<Node> visitedNodes = new HashSet<Node>();
for (int i = 1; i < path.Count; i++)
{
var pathCellPosition = path[i];
visitedNodes.Add(path[i]);
pathCellPosition.Position = new Vector3(pathCellPosition.Position.x, pathPrefab.transform.position.y, pathCellPosition.Position.z);
List<Node> neighbours = GetNeighbours(path[i]);
int fCost = DistanceFromStart(pathCellPosition) + HCostDistanceFromEndNode(pathCellPosition) + pathCellPosition.Cost;
pathPrefab.GetComponentInChildren<Text>().text = fCost.ToString();
var p = Instantiate(pathPrefab, pathCellPosition.Position, Quaternion.identity, PathCells);
for(int x = 0; x < neighbours.Count; x++)
{
var node = neighbours[x];
if (visitedNodes.Contains(node))
{
continue;
}
visitedNodes.Add(node);
fCost = DistanceFromStart(node) + HCostDistanceFromEndNode(node) + node.Cost;
neighbourPrefab.GetComponentInChildren<Text>().text = fCost.ToString();
Instantiate(neighbourPrefab, node.Position, Quaternion.identity, p.transform);
List<Node> NeighBourOfNeighbourNodes = GetNeighbours(node);
if(NeighBourOfNeighbourNodes.Count > 0)
{
for (int y = 0; y < NeighBourOfNeighbourNodes.Count; y++)
{
var neighbourNode = NeighBourOfNeighbourNodes[y];
if (visitedNodes.Contains(neighbourNode))
{
continue;
}
visitedNodes.Add(neighbourNode);
fCost = DistanceFromStart(neighbourNode) + HCostDistanceFromEndNode(neighbourNode) + neighbourNode.Cost;
nofn.GetComponentInChildren<Text>().text = fCost.ToString();
Instantiate(nofn, neighbourNode.Position, Quaternion.identity, p.transform);
}
}
}
If I don't draw neighbour of neighbours, this looks right. But if I do neighbour of neighbours the purple ones don't appear as much as they should.
Before:
After:
A typical simple solution is to check if the node is part of the path before adding. A HashSet can be good for this, but you will need something that can be reliably compared.
Perhaps something like this:
var visitedNodes = new HashSet<Node>(path);
for (int i = 1; i < path.Count; i++)
{
var pathCellPosition = path[i];
pathCellPosition.Position = new Vector3(pathCellPosition.Position.x, pathPrefab.transform.position.y, pathCellPosition.Position.z);
List<Node> neighbours = GetNeighbours(pathCellPosition);
var p = Instantiate(pathPrefab, pathCellPosition.Position, Quaternion.identity, PathCells);
foreach (Node n in neighbours)
{
if(visitedNodes.Contains(n))
continue;
visitedNodes.Add(n);
neighbourPrefab.GetComponentInChildren<Text>().text = n.Cost.ToString();
Instantiate(neighbourPrefab, n.Position, Quaternion.identity, p.transform);
}
}
You can also use the same approach to ensure a neighbour is not added twice.
If you want multiple levels from the node you might need to do something like a breath first search, ideally keeping track of the distance from the original path. The following generic code should traverse the graph in a breadth first manner, just use the path as the first parameter, and the GetNeighbours method for the second.
public static IEnumerable<(T Node, int Distance)> GetNeighborsWithDistance<T>(IEnumerable<T> self, Func<T, IEnumerable<T>> selector)
{
var stack = new Queue<(T Node, int Distance)>();
var visited = new HashSet<T>();
foreach (var node in self)
{
stack.Enqueue((node, 0));
}
while (stack.Count > 0)
{
var current = stack.Dequeue();
if(visited.Contains(current.Node))
continue;
yield return current;
visited.Add(current.Node);
foreach (var child in selector(current.Node))
{
stack.Enqueue((child, current.Distance+ 1));
}
}
}
This should be guaranteed to return nodes in order of distance from the original path, so just do .TakeWhile(p => p.Distance < DesiredDistanceFromOriginalPath) to get as many nodes as you want.

Check if 2 objects from an array overlap and change their position on y if they do

So how can I update the position every time I call the StartRandomizingRightSpikePosition
private bool CheckOverlap(GameObject o1, GameObject o2)
{
return spikeRight.Select(t => t.GetComponent<Collider>().bounds.Intersects(t.GetComponent<Collider>().bounds)).FirstOrDefault();
}
public void StartRandomizingRightSpikesPosition()
{
foreach (var t in spikeRight)
{
foreach (var t1 in spikeRight)
{
if (t == t1) continue;
if (!CheckOverlap(t, t1)) continue;
yPosition = Random.Range(-7, 7);
var position = t1.transform.position;
desiredPosition = new Vector3(position.x, yPosition, position.z);
t1.transform.position = desiredPosition;
Debug.Log(t.gameObject + " intersects " + t1.gameObject);
}
}
}
The short answer is yes but I'm not sure you would want too. I'm not sure you're going to find a way to do this efficiently and you might be better off finding a way to generate the objects such that this step is not necessary.
I can't tell from your question how the objects are actually stored so I'm going to provide some sample code that just deals with a simple array of Rectangles. You should be able to adapt it to your specifics.
I tried to make it slightly more efficient by not checking both t1 == t and t == t1.
const int Bounds = 1000;
static bool RemovedOverlappingRects(Rectangle[] rects)
{
for (int pos = 0; pos < rects.Length; ++pos)
{
for (int check = pos +1; check < rects.Length; ++check)
{
var r1 = rects[pos];
var r2 = rects[check];
if (r1.IntersectsWith(r2))
{
r2.Y = Rng.Next(1, Bounds);
rects[check] = r2;
Console.WriteLine($"{pos} overlaps with {check}");
return true;
}
}
}
return false;
}
Once we've randomly generated a new rectangle we have to start over. Which means invoking the above method in a loop.
var rects = GetRandomeRects(20).ToArray();
while (RemovedOverlappingRects(rects))
;
Because of the random movement I'm not certain you can guarantee this will always end. If you can deal with the non-random look of the results changing it to stack the overlaps would I believe always finish. That would be this:
r2.Y = r1.Y + r1.Height + 1;
in place of
r2.Y = Rng.Next(1, Bounds);
But even then you're still dealing with a very unpredictable run time due to the random input.
Maybe someone else can show us a more efficient way...

How can I speed up this image matching code in EmguCV C# code?

Here's the code. See the commented line for where possible optimization could be (at the foreach). Anyone have suggestions on improving the speed here?
public void FindMatches(Matrix<float> dbDescriptors, Matrix<float> queryDescriptors, ref IList<IndecesMapping> imap)
{
var indices = new Matrix<int>(queryDescriptors.Rows, 2); // matrix that will contain indices of the 2-nearest neighbors found
var dists = new Matrix<float>(queryDescriptors.Rows, 2); // matrix that will contain distances to the 2-nearest neighbors found
// create FLANN index with 4 kd-trees and perform KNN search over it look for 2 nearest neighbours
var flannIndex = new Index(dbDescriptors, 4);
flannIndex.KnnSearch(queryDescriptors, indices, dists, 2, 24);
for (int i = 0; i < indices.Rows; i++)
{
// filter out all inadequate pairs based on distance between pairs
if (dists.Data[i, 0] < (0.5 * dists.Data[i, 1]))
{
// find image from the db to which current descriptor range belongs and increment similarity value.
// in the actual implementation this should be done differently as it's not very efficient for large image collections.
foreach (var img in imap)
{
if (img.IndexStart <= indices[i, 0] && img.IndexEnd >= indices[i, 0])
{
img.Similarity++;
break;
}
}
}
}
}
You can improve your work by changing foreach loop as following:
foreach (var img in imap)
{
if (img.IndexStart <= indices[i, 1] && img.IndexEnd >= indices[i, 1])
{
img.Similarity++;
break;
}
}
Then it is working fine

Optimisation of route finding code

Small bit of background first. I am developing a system that generates a "route" between locations. Locations have a pre-defined list of neighbours not limited to those adjacent to it. The search can safely assume that by picking the closest neighbour (numerically) to the target destination, it is making the optimal move towards it.
I have working code as shown below:
public Route GetRoute(int StartPoint, int Destination)
{
Route returnRoute = new Route();
returnRoute.steps = new List<int>();
bool locationReached = false;
int selectedNeighbour;
int distanceFromTarget;
int currentPoint = StartPoint; // set the current point to the start point
while (!locationReached)
{
selectedNeighbour = 0;
distanceFromTarget = 5000; // nominal amount guaranteed to be overwritten
var neighbours = locations.FirstOrDefault(l => l.LocationID == currentPoint).Neighbours;
for (int i = 0; i < neighbours.Length; i++)
{
// get the current neighbours, then check proximity
int currentNeighbour = neighbours[i];
int tempDistance = Math.Abs( currentNeighbour - Destination );
// if nearer than previous neighbour, set it as the chosen location
if ( tempDistance < distanceFromTarget )
{
distanceFromTarget = tempDistance;
selectedNeighbour = currentNeighbour;
// if the selected neighbour is the destination, we're done
if ( selectedNeighbour == Destination )
locationReached = true;
}
} // for
// add the selected neighbour if we found one
if ( selectedNeighbour != 0 )
{
currentPoint = selectedNeighbour;
returnRoute.steps.Add(selectedNeighbour);
}
else
{
Debug.Log ("No Route Found");
return returnRoute;
}
} // while
return returnRoute;
}
My question is regarding the loop of the neighbours (int[]) variable. How can this best be optimised? I've seen some use of linq and ordering, but also comments that this approach might be inefficient. I need efficiency over neatness here.
Many thanks.

C# alpha-beta pruning with reference

So here is the problem. I'm working on a combat-simulator for an AI based game. The AI should calculate the best move for him when the enemy makes his best move against every move.
My team has X units with 5 possible moves and my opponent has Y units with 5 possible moves. X and Y > 0
using alpha-beta pruning we want to generate each possible outcome and take the best one out in the end. The problem is the fact that we save each outcome into a situation, this situation stores lists but the lists contain references to the objects stored, this makes them save their moves into the same situation (all 5 moves of 1 unit)
Imagine 2 of our units and one of theirs. We create a situation and add one unit with 1 of the 5 directions to it. Then for our second unit we add one direction, then for the enemy unit. Now we got the end situation we want to save this. Then from the situation we had in OUR second unit (so without the enemy unit) we want to add a different move for the enemy to the situation and save that new situation if it was better. But since C# uses references for lists this situation is the situation with the other enemy move included aswell.
Code is a bit large but I'm really stuck here so I'd hope if anyone has some spare time to help me out with ideas to fix this.
public Situation RecursiveMaxTree(List<SimAnt> undone, int index, bool[,] positions, Situation situation)
{
SimAnt front = undone[index];
int max = situation.Max;
List<SimAnt> bestmoves = new List<SimAnt>();
int frontY = front.position.Y;
int frontX = front.position.X;
if (!front.isEnemy() && undone[index + 1].isEnemy())//cutoff activated----------------------------------------------------------------------------
{
foreach (Direction direction in directions)
{
int newX = GameState.WrapHorizonal(frontX + direction.toVector().X);
int newY = GameState.WrapVertical(frontY + direction.toVector().Y);
if (!positions[newX, newY] && map.isNotWater(newX, newY))
{
//add the updated ant to the new allAnts list
positions[newX, newY] = true;
List<SimAnt> newallAnts = situation.GetList;
Friend newant = new Friend(newX, newY);
newant.direction = direction;
newallAnts.Add(newant);
Situation current = RecursiveMinTree(undone, index + 1, positions, new Situation(newallAnts, max));//geeft min van alle enemy options
positions[newX, newY] = false;
if (current.Max > max)
{
max = current.Max;
bestmoves = current.GetList;
}
}
}
}
else //max-------------------------------------------------------------------------------------------------------------------------------------
{
foreach (Direction direction in directions)
{
int newX = GameState.WrapHorizonal(frontX + direction.toVector().X);
int newY = GameState.WrapVertical(frontY + direction.toVector().Y);
if (!positions[newX, newY] && map.isNotWater(newX, newY))
{
//add the updated ant to the new allAnts list
positions[newX, newY] = true;
List<SimAnt> newallAnts = situation.GetList.Clone();
Friend newant = new Friend(newX, newY);
newant.direction = direction;
newallAnts.Add(newant);
Situation current = RecursiveMaxTree(undone, index + 1, positions, new Situation(newallAnts, max));//geeft min van alle enemy options
positions[newX, newY] = false;
if (current.Max > max)
{
max = current.Max;
bestmoves = current.GetList;
}
}
}
}
return new Situation(bestmoves, max);
}
Situation RecursiveMinTree(List<SimAnt> undone, int index, bool[,] positions, Situation situation)
{
SimAnt front = undone[index];
int max = situation.Max;
int frontY = front.position.Y;
int frontX = front.position.X;
int currentmin = 100;
foreach (Direction direction in directions)
{
int newX = GameState.WrapHorizonal(frontX + direction.toVector().X);
int newY = GameState.WrapVertical(frontY + direction.toVector().Y);
if (!positions[newX, newY] && map.isNotWater(newX, newY))
{
//add the updated ant to the new allAnts list
List<SimAnt> newallAnts = situation.GetList;
Foe newant = new Foe(newX, newY);
newallAnts.Add(newant);
if (index >= undone.Count - 1)
{
return new Situation(situation.GetList, CalculateBattleValue(situation.GetList));
}
else
{
positions[newX, newY] = true;
Situation current = RecursiveMinTree(undone, index + 1, positions, new Situation(newallAnts, max));//geeft min van alle enemy options
positions[newX, newY] = false;
if (current.Max < max)
return current;
else if (current.Max < currentmin)
{
currentmin = current.Max;
}
}
}
}
return new Situation(situation.GetList, currentmin);
}
class Situation
{
public Situation(List<SimAnt> ants, int max)
{
this.max = max;
this.ants = ants;
}
List<SimAnt> ants;
int max;
public List<SimAnt> GetList
{ get { return ants; } }
public int Max
{ get { return max; } }
}
Kind regards, me

Categories