Skip to content

Commit c424714

Browse files
author
Зелёный Андрей Сергеевич
committed
Добавлено свойство для предельной величины уклона MaxSlope при поиске пути на поверхности. Добавлен метод Slope() для вычисления уклона между вершинами графа. Рефакторинг конструкторов, метода для поиска валидных соседей. Корректировка описаний методов и названий некоторых методов. Удален коэффициент gamma. Другие мелкие исравления
1 parent e045710 commit c424714

1 file changed

Lines changed: 110 additions & 32 deletions

File tree

Graph.cs

Lines changed: 110 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Linq;
45

56
namespace DijkstraAlgorithm
@@ -22,9 +23,14 @@ public class Graph
2223
/// Количество вершин по оси Oy
2324
/// </summary>
2425
public int M { get; }
26+
/// <summary>
27+
/// Матрица вершин графа
28+
/// </summary>
2529
public Vertex[,] Vertices { get; }
26-
public Func<double, double, double> SurfaceFunc { get; }
27-
public double gamma { get; }
30+
/// <summary>
31+
/// Предельная величина уклона, необходимая для обхода препятствий, в градусах
32+
/// </summary>
33+
public double MaxSlope { get; }
2834

2935
/// <summary>
3036
/// Возвращает (физические) координаты вершины с учетом координаты узла и шага регулярной сетки
@@ -42,7 +48,7 @@ public class Graph
4248
}
4349

4450
/// <summary>
45-
/// Возвращает вес ребра, соединяющего две соседние (смежные) вершины графа
51+
/// Возвращает вес ребра, соединяющего две соседние (смежные) вершины графа (по факту это расстояние между точками поверхности)
4652
/// </summary>
4753
/// <param name="v1">Первая вершина</param>
4854
/// <param name="v2">Вторая вершина</param>
@@ -56,16 +62,17 @@ double Weight(Vertex v1, Vertex v2)
5662
double yDiff = x1y1.Item2 - x2y2.Item2;
5763
double zDiff = v1.Height - v2.Height;
5864

59-
double sumOfSquares = Math.Pow(xDiff, 2.0) + Math.Pow(yDiff, 2.0) + gamma * Math.Pow(zDiff, 2.0);
65+
double sumOfSquares = Math.Pow(xDiff, 2.0) + Math.Pow(yDiff, 2.0) + Math.Pow(zDiff, 2.0);
6066

6167
return Math.Sqrt(sumOfSquares);
6268
}
6369

6470
/// <summary>
65-
/// Возвращает кратчайший путь между двумя заданными вершинами графа
71+
/// Возвращает кратчайший путь между двумя заданными вершинами графа и его длину
6672
/// </summary>
6773
/// <param name="startPoint">координаты стартовой вершины</param>
6874
/// <param name="goalPoint">координаты целевой вершины</param>
75+
/// <param name="shortestPathLength">длина пути</param>
6976
/// <returns></returns>
7077
public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalPoint, out double shortestPathLength)
7178
{
@@ -86,8 +93,8 @@ public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalP
8693

8794
while (current != null)
8895
{
89-
// Находим смежные и не посещенные вершины к текущей вершине
90-
List<Vertex> neighbors = GetUnvisitedNeighbors(current);
96+
// Находим подходящих (годных) соседей: которые еще не посещены, не являются препятствиями и т.п.
97+
List<Vertex> neighbors = GetValidNeighbors(current);
9198

9299
foreach (Vertex neighbor in neighbors)
93100
{
@@ -99,7 +106,7 @@ public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalP
99106
}
100107
}
101108

102-
// После того как все соседи рассмотрены (всем соседям расставлены метки), помечаем текущую вершину как посещенную
109+
// После того как все подходящие соседи рассмотрены (им расставлены метки), помечаем текущую вершину как посещенную
103110
current.IsVisited = true;
104111
// и добавляем ее в список посещенных вершин
105112
visitedVertices.Add(current);
@@ -112,7 +119,7 @@ public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalP
112119
}
113120

114121
/// <summary>
115-
/// Формирует и возвращает кратчайший путь
122+
/// Формирует кратчайший путь, начиная с целевой вершины и заканчивая стартовой
116123
/// </summary>
117124
/// <param name="goal">целевая вершина</param>
118125
/// <returns></returns>
@@ -134,48 +141,55 @@ private List<Point2D> GetShortestPath(Vertex goal)
134141
}
135142

136143
/// <summary>
137-
/// Возвращает текущую вершину используя список посещенных вершин
144+
/// Возвращает текущую вершину, используя список посещенных вершин и подходящих соседей (которые валидны и не являются целевой вершиной)
138145
/// </summary>
139146
/// <param name="visitedVertices">список посещенных вершин</param>
140147
/// <returns></returns>
141148
private Vertex GetCurrent(List<Vertex> visitedVertices)
142149
{
143-
List<Vertex> unvisitedAndNotGoalNeighbors = new List<Vertex>();
150+
List<Vertex> validAndNotGoalNeighbors = new List<Vertex>();
144151

145152
foreach (Vertex v in visitedVertices)
146-
if (HasUnvisitedAndNotGoalNeighbors(v, out unvisitedAndNotGoalNeighbors))
153+
if (HasValidAndNotGoalNeighbors(v, out validAndNotGoalNeighbors))
147154
break;
148155

149156
// Если не нашлось ни одного подходящего соседа, значит мы дошли до финальной вершины
150-
if (!unvisitedAndNotGoalNeighbors.Any())
157+
if (!validAndNotGoalNeighbors.Any())
151158
return null;
152159

153160
// Находим и возвращаем соседа с минимальной меткой
154-
double minLabel = unvisitedAndNotGoalNeighbors.Min(v => v.Label);
155-
Vertex newCurrent = unvisitedAndNotGoalNeighbors.First(v => v.Label == minLabel);
161+
double minLabel = validAndNotGoalNeighbors.Min(v => v.Label);
162+
Vertex newCurrent = validAndNotGoalNeighbors.First(v => v.Label == minLabel);
156163

157164
return newCurrent;
158165
}
159166

160167
/// <summary>
161-
/// Возвращает true, если у текущей вершины имеются непосещенные соседи (среди которых нет целевой вершины), иначе false,
162-
/// а также сам список непосещенных соседей
168+
/// Возвращает true, если у текущей вершины имеются валидные соседи, за исключением целевой вершины, иначе false,
169+
/// а также сам список этих соседей
163170
/// </summary>
164171
/// <param name="vertex">текущая вершина</param>
165-
/// <param name="unvisitedAndNotGoalNeighbors">список непосещенных соседей</param>
172+
/// <param name="validAndNotGoalNeighbors">список подходящих соседей</param>
166173
/// <returns></returns>
167-
private bool HasUnvisitedAndNotGoalNeighbors(Vertex vertex, out List<Vertex> unvisitedAndNotGoalNeighbors)
174+
private bool HasValidAndNotGoalNeighbors(Vertex vertex, out List<Vertex> validAndNotGoalNeighbors)
168175
{
169-
unvisitedAndNotGoalNeighbors = GetUnvisitedNeighbors(vertex).Where(v => !v.IsGoal).ToList();
170-
return unvisitedAndNotGoalNeighbors.Any();
176+
validAndNotGoalNeighbors = GetValidNeighbors(vertex).Where(v => !v.IsGoal).ToList();
177+
return validAndNotGoalNeighbors.Any();
171178
}
172179

173180
/// <summary>
174-
/// Возвращает все смежные вершины для текущей, которые не посещены
181+
/// Возвращает величину уклона между двумя вершинами в градусах
175182
/// </summary>
176-
/// <param name="current">текущая вершина</param>
183+
/// <param name="v1">первая вершина</param>
184+
/// <param name="v2">вторая вершина</param>
177185
/// <returns></returns>
178-
private List<Vertex> GetUnvisitedNeighbors(Vertex current) => GetAllAdjacentVertices(current).Where(v => !v.IsVisited && !v.IsObstacle).ToList();
186+
private double Slope(Vertex v1, Vertex v2)
187+
{
188+
double hypotenuse = Weight(v1, v2); // Вес ребра - это и есть по факту расстояние между точками
189+
double zDiffAbs = Math.Abs(v1.Height - v2.Height); // Модуль разности по высоте
190+
191+
return Math.Asin(zDiffAbs / hypotenuse) * 180.0 / Math.PI; // Переводим радианы в градусы
192+
}
179193

180194
#region Методы для поиска соседней вершины в зависимости от направления
181195
private Vertex GetTopVertex(Vertex v) => Vertices[v.Coordinate.i, v.Coordinate.j + 1];
@@ -200,6 +214,20 @@ private bool HasUnvisitedAndNotGoalNeighbors(Vertex vertex, out List<Vertex> unv
200214
private bool IsVertexOnTheLeftSide(Vertex v1) => v1.Coordinate.i == 0;
201215
#endregion
202216

217+
/// <summary>
218+
/// Возвращает для текущей вершины подходящих (валидных) соседей
219+
/// </summary>
220+
/// <param name="current">текущая вершина</param>
221+
/// <returns></returns>
222+
private List<Vertex> GetValidNeighbors(Vertex current)
223+
{
224+
// Из всех смежных вершин отбираем те, которые
225+
// 1. Еще не посещены
226+
// 2. Не являются вершинами-препятствиями
227+
// 3. Наклон к которым меньше заданной величины (например, 30 градусов)
228+
return GetAllAdjacentVertices(current).Where(v => !v.IsVisited && !v.IsObstacle && Slope(v, current) < MaxSlope).ToList();
229+
}
230+
203231
/// <summary>
204232
/// Возвращает все смежные вершины к рассматриваемой вершине
205233
/// </summary>
@@ -302,32 +330,82 @@ private List<Vertex> GetAllAdjacentVertices(Vertex vertex)
302330
};
303331
}
304332

305-
public Graph(double dx, double dy, int N, int M, Func<double, double, double> SurfaceFunc, double gamma = 1.0)
333+
/// <summary>
334+
/// Задает высоты определенных вершин графа. Необходим для создания (имитации) на карте местности сооружений/зданий
335+
/// </summary>
336+
/// <param name="bottomLeftCoordinate">левая нижняя координата сооружения. От нее будет начинаться отсчет</param>
337+
/// <param name="width">ширина сооружения (количество вершин, которое будет занимать сооружение по оси Ox)</param>
338+
/// <param name="length">длина сооружения (количество вершин, которое будет занимать сооружение по оси Oy)</param>
339+
/// <param name="height">высота сооружения</param>
340+
public void CreateBuilding(Point2D bottomLeftCoordinate, int width, int length, double height)
341+
{
342+
for (int i = bottomLeftCoordinate.i; i < bottomLeftCoordinate.i + width; i++)
343+
{
344+
for (int j = bottomLeftCoordinate.j; j < bottomLeftCoordinate.j + length; j++)
345+
Vertices[i, j].Height = height;
346+
}
347+
}
348+
349+
/// <summary>
350+
/// Записывает поверхность в файл
351+
/// </summary>
352+
/// <param name="fileName">имя файла</param>
353+
public void WriteSurfaceToFile(string fileName)
354+
{
355+
using (StreamWriter outputFile = new StreamWriter(fileName))
356+
{
357+
for (int j = 0; j < M; j++)
358+
{
359+
for (int i = 0; i < N; i++)
360+
outputFile.Write(Vertices[i, j].Height.ToString() + ";");
361+
362+
outputFile.WriteLine();
363+
}
364+
}
365+
}
366+
367+
/// <summary>
368+
/// Инициализирует граф с помощью композиций функций Гаусса
369+
/// </summary>
370+
/// <param name="dx">величина шага по оси Ox</param>
371+
/// <param name="dy">величина шага по оси Oy</param>
372+
/// <param name="N">количество вершин по оси Ox</param>
373+
/// <param name="M">количество вершин по оси Oy</param>
374+
/// <param name="MaxSlope">предельная величина уклона</param>
375+
/// <param name="gaussianParameters">список параметров для вычисления Гауссианов</param>
376+
public Graph(double dx, double dy, int N, int M, double MaxSlope, params GaussianParameter[] gaussianParameters)
306377
{
307-
this.N = N;
308-
this.M = M;
309378
this.dx = dx;
310379
this.dy = dy;
311-
this.SurfaceFunc = SurfaceFunc;
312-
this.gamma = gamma;
380+
this.N = N;
381+
this.M = M;
382+
this.MaxSlope = MaxSlope;
313383

314384
Vertices = new Vertex[N, M];
315385

316386
for (int j = 0; j < M; j++)
317387
for (int i = 0; i < N; i++)
318388
{
319-
double height = SurfaceFunc(i * dx, j * dy);
389+
double height = 0.0;
390+
foreach (GaussianParameter gp in gaussianParameters)
391+
height += Surface.Gaussian(i * dx, j * dy, gp);
392+
320393
Vertices[i, j] = new Vertex(i, j, Height: height);
321394
}
322395
}
323396

324-
public Graph(int[,] obstacleMatrix)
397+
/// <summary>
398+
/// Инициализирует граф с помощью матрицы препятствий
399+
/// </summary>
400+
/// <param name="obstacleMatrix">матрица с препятствиями</param>
401+
/// <param name="MaxSlope">предельная величина уклона</param>
402+
public Graph(int[,] obstacleMatrix, double MaxSlope = 20.0)
325403
{
326404
this.N = Convert.ToInt32(obstacleMatrix.GetLongLength(1));
327405
this.M = Convert.ToInt32(obstacleMatrix.GetLongLength(0));
328406
this.dx = 1.0;
329407
this.dy = 1.0;
330-
this.gamma = 1.0;
408+
this.MaxSlope = MaxSlope;
331409

332410
Vertices = new Vertex[N, M];
333411

0 commit comments

Comments
 (0)