235 lines
7.6 KiB
C#
235 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Roy_T.AStar.Graphs;
|
|
using Roy_T.AStar.Primitives;
|
|
|
|
namespace Roy_T.AStar.Grids
|
|
{
|
|
public sealed class Grid
|
|
{
|
|
private readonly Node[,] Nodes;
|
|
|
|
public static Grid CreateGridWithLateralConnections(GridSize gridSize, Size cellSize, Velocity traversalVelocity)
|
|
{
|
|
CheckArguments(gridSize, cellSize, traversalVelocity);
|
|
|
|
var grid = new Grid(gridSize, cellSize);
|
|
|
|
grid.CreateLateralConnections(traversalVelocity);
|
|
|
|
return grid;
|
|
}
|
|
|
|
public static Grid CreateGridWithDiagonalConnections(GridSize gridSize, Size cellSize, Velocity traversalVelocity)
|
|
{
|
|
CheckArguments(gridSize, cellSize, traversalVelocity);
|
|
|
|
var grid = new Grid(gridSize, cellSize);
|
|
|
|
grid.CreateDiagonalConnections(traversalVelocity);
|
|
|
|
return grid;
|
|
}
|
|
|
|
public static Grid CreateGridWithLateralAndDiagonalConnections(GridSize gridSize, Size cellSize, Velocity traversalVelocity)
|
|
{
|
|
CheckArguments(gridSize, cellSize, traversalVelocity);
|
|
|
|
var grid = new Grid(gridSize, cellSize);
|
|
|
|
grid.CreateDiagonalConnections(traversalVelocity);
|
|
grid.CreateLateralConnections(traversalVelocity);
|
|
|
|
return grid;
|
|
}
|
|
|
|
private static void CheckArguments(GridSize gridSize, Size cellSize, Velocity defaultSpeed)
|
|
{
|
|
if (gridSize.Columns < 1)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(gridSize), $"Argument {nameof(gridSize.Columns)} is {gridSize.Columns} but should be >= 1");
|
|
}
|
|
|
|
if (gridSize.Rows < 1)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(gridSize), $"Argument {nameof(gridSize.Rows)} is {gridSize.Rows} but should be >= 1");
|
|
}
|
|
|
|
if (cellSize.Width <= Distance.Zero)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(cellSize), $"Argument {nameof(cellSize.Width)} is {cellSize.Width} but should be > {Distance.Zero}");
|
|
}
|
|
|
|
if (cellSize.Height <= Distance.Zero)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(cellSize), $"Argument {nameof(cellSize.Height)} is {cellSize.Height} but should be > {Distance.Zero}");
|
|
}
|
|
|
|
if (defaultSpeed.MetersPerSecond <= 0.0f)
|
|
{
|
|
throw new ArgumentOutOfRangeException(
|
|
nameof(defaultSpeed), $"Argument {nameof(defaultSpeed)} is {defaultSpeed} but should be > 0.0 m/s");
|
|
}
|
|
}
|
|
|
|
private Grid(GridSize gridSize, Size cellSize)
|
|
{
|
|
this.GridSize = gridSize;
|
|
this.Nodes = new Node[gridSize.Columns, gridSize.Rows];
|
|
|
|
this.CreateNodes(cellSize);
|
|
}
|
|
|
|
private void CreateNodes(Size cellSize)
|
|
{
|
|
for (var x = 0; x < this.Columns; x++)
|
|
{
|
|
for (var y = 0; y < this.Rows; y++)
|
|
{
|
|
this.Nodes[x, y] = new Node(Position.FromOffset(cellSize.Width * x, cellSize.Height * y));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateLateralConnections(Velocity defaultSpeed)
|
|
{
|
|
for (var x = 0; x < this.Columns; x++)
|
|
{
|
|
for (var y = 0; y < this.Rows; y++)
|
|
{
|
|
var node = this.Nodes[x, y];
|
|
|
|
if (x < this.Columns - 1)
|
|
{
|
|
var eastNode = this.Nodes[x + 1, y];
|
|
node.Connect(eastNode, defaultSpeed);
|
|
eastNode.Connect(node, defaultSpeed);
|
|
}
|
|
|
|
if (y < this.Rows - 1)
|
|
{
|
|
var southNode = this.Nodes[x, y + 1];
|
|
node.Connect(southNode, defaultSpeed);
|
|
southNode.Connect(node, defaultSpeed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateDiagonalConnections(Velocity defaultSpeed)
|
|
{
|
|
for (var x = 0; x < this.Columns; x++)
|
|
{
|
|
for (var y = 0; y < this.Rows; y++)
|
|
{
|
|
var node = this.Nodes[x, y];
|
|
|
|
if (x < this.Columns - 1 && y < this.Rows - 1)
|
|
{
|
|
var southEastNode = this.Nodes[x + 1, y + 1];
|
|
node.Connect(southEastNode, defaultSpeed);
|
|
southEastNode.Connect(node, defaultSpeed);
|
|
}
|
|
|
|
if (x > 0 && y < this.Rows - 1)
|
|
{
|
|
var southWestNode = this.Nodes[x - 1, y + 1];
|
|
node.Connect(southWestNode, defaultSpeed);
|
|
southWestNode.Connect(node, defaultSpeed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public GridSize GridSize { get; }
|
|
|
|
public int Columns => this.GridSize.Columns;
|
|
|
|
public int Rows => this.GridSize.Rows;
|
|
|
|
public INode GetNode(GridPosition position) => this.Nodes[position.X, position.Y];
|
|
|
|
public IReadOnlyList<INode> GetAllNodes()
|
|
{
|
|
var list = new List<INode>(this.Columns * this.Rows);
|
|
|
|
for (var x = 0; x < this.Columns; x++)
|
|
{
|
|
for (var y = 0; y < this.Rows; y++)
|
|
{
|
|
list.Add(this.Nodes[x, y]);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public void DisconnectNode(GridPosition position)
|
|
{
|
|
var node = this.Nodes[position.X, position.Y];
|
|
|
|
foreach (var outgoingEdge in node.Outgoing)
|
|
{
|
|
var opposite = outgoingEdge.End;
|
|
opposite.Incoming.Remove(outgoingEdge);
|
|
}
|
|
|
|
node.Outgoing.Clear();
|
|
|
|
foreach (var incomingEdge in node.Incoming)
|
|
{
|
|
var opposite = incomingEdge.Start;
|
|
opposite.Outgoing.Remove(incomingEdge);
|
|
}
|
|
|
|
node.Incoming.Clear();
|
|
}
|
|
|
|
public void RemoveDiagonalConnectionsIntersectingWithNode(GridPosition position)
|
|
{
|
|
var left = new GridPosition(position.X - 1, position.Y);
|
|
var top = new GridPosition(position.X, position.Y - 1);
|
|
var right = new GridPosition(position.X + 1, position.Y);
|
|
var bottom = new GridPosition(position.X, position.Y + 1);
|
|
|
|
if (this.IsInsideGrid(left) && this.IsInsideGrid(top))
|
|
{
|
|
this.RemoveEdge(left, top);
|
|
this.RemoveEdge(top, left);
|
|
}
|
|
|
|
if (this.IsInsideGrid(top) && this.IsInsideGrid(right))
|
|
{
|
|
this.RemoveEdge(top, right);
|
|
this.RemoveEdge(right, top);
|
|
}
|
|
|
|
if (this.IsInsideGrid(right) && this.IsInsideGrid(bottom))
|
|
{
|
|
this.RemoveEdge(right, bottom);
|
|
this.RemoveEdge(bottom, right);
|
|
}
|
|
|
|
if (this.IsInsideGrid(bottom) && this.IsInsideGrid(left))
|
|
{
|
|
this.RemoveEdge(bottom, left);
|
|
this.RemoveEdge(left, bottom);
|
|
}
|
|
}
|
|
|
|
public void RemoveEdge(GridPosition from, GridPosition to)
|
|
{
|
|
var fromNode = this.Nodes[from.X, from.Y];
|
|
var toNode = this.Nodes[to.X, to.Y];
|
|
|
|
fromNode.Disconnect(toNode);
|
|
}
|
|
|
|
private bool IsInsideGrid(GridPosition position) => position.X >= 0 && position.X < this.Columns && position.Y >= 0 && position.Y < this.Rows;
|
|
}
|
|
}
|