11using System ;
22using System . Collections . Generic ;
3+ using System . IO ;
34using System . Linq ;
45
56namespace 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