![]() |
////////////////////////////////////////////////////////////////////
// //
// Алгоритм обхода препятствий: //
// http://www.delphikingdom.com/asp/viewitem.asp?catalogID=166 //
// исходник взят там же //
// Доработано: Виктор Некрасов //
// //
////////////////////////////////////////////////////////////////////
{.$Define Full}
unit PathFinder;
interface
Uses Windows, Types, Math, SysUtils,
{$IfDef Full}
DataSet2, ErmTypes, Pts, GeomTypes,
{$EndIf}
Graphics;
Type
TTile = packed record
value : Double;
TerrType : byte;
End;
TTileProt = record
Status : byte;
gval : Double;
fval : Double;
Prev : TPoint;
End;
THead = packed record
Width,Height:Integer;
End;
const
ttClear = 0; // проходимая клетка - самый хорошо проходимый тип территории
ttType1 = 1; // тип территории 1 - самый плохо проходимый тип территории
ttType2 = 2; // тип территории 2
ttType3 = 3; // тип территории 3
ttType4 = 4; // тип территории 4
ttType5 = 5; // тип территории 5
ttWall = 6; // препятствие
tsUnvisited = 0; // непосещенная точка
tsPath = 1; // путь
tsBound = 2; // граничная точка
tsPassed = 3; // проверенная точка
tsStart = 4; // точка старта
tsFinish = 5; // точка финиша
Courses : array [1..8] of TPoint=((X:0;Y:-1),(X:1;Y:-1),(X:1;Y:0), (X:1;Y:1),
(X:0;Y:1), (X:-1;Y:1),(X:-1;Y:0),(X:-1;Y:-1));
NullPoint : TPoint = (X:-1;Y:-1);
NullTile:TTile = ( value:0; TerrType:ttClear );
// цвета для создания картинки SaveToImg
TerrColors : array [0..6] of TColor = (clWhite,$999999,$AAAAAA,$BBBBBB,$CCCCCC,$DDDDDD,$555555);
StatusColors : array [0..5] of TColor = (clWhite,$FF0000,$00DDDD,$00BB00,clGreen,clRed);
Type
TMap = Class
private
FMap : Array of TTile; //массив точек карты (для наружного применения)
FMapPro : Array of TTileProt;//служебный массив точек для работы алгоритма поиска
FHeight : Integer;
FWidth : Integer;
FBound : array of TPoint;// служебный массив границ
FBSize : Integer; // размер служебного массива границ
FSrc,FDst : TPoint;
FHead : THead;
function GetMap(col,row:Integer):TTile;
procedure SetMap(col,row:Integer;T:TTile);
function GetMapStatus(col,row:Integer):byte;
procedure SetMapStatus(col,row:Integer;T:byte);
Function FindMin: integer;
procedure AddToBound(Point:TPoint);
public
ErrorStr : String;
PathLength : Integer;
Time : Cardinal;
Xmin,Ymax,Step : Double;// геопризязка верхнего левого угла (0,0) и шаг
constructor Create(Height,Width:Integer);
procedure Clear; // очистка всего поля
procedure Wall; // создать стену по крайним клеткам поля
procedure Unvisit;// стереть поисковые данные - применить перед поиском!
Function GoodPoint(a:TPoint):boolean ;
Function FindPath(Src,Dst: TPoint): boolean;
// делает список точек пути, результат объект - массив точек от Dst к Src
{$IfDef Full}Function PathToLine:TPXYZ;
Function SaveToImg(const name:String):Boolean;
{$EndIf}
Function SaveToFile(const name:String):Boolean;
Function LoadFromFile(const name:String):Boolean;
property Map[col,row:Integer]:TTile read GetMap write SetMap;
property MapStatus[col,row:Integer]:byte read GetMapStatus write SetMapStatus;
End;
implementation
constructor TMap.Create(Height,Width:Integer);
Begin
inherited Create;
FHeight := Height;
FWidth := Width;
SetLength(FMap,FHeight*FWidth);
SetLength(FMapPro,FHeight*FWidth);
End;
function TMap.GetMap(col,row:Integer):TTile;
Begin
If (col<0)or(col>=FWidth)or(row<0)or(row>=FHeight) Then Result := NullTile
Else Result := FMap[row*FWidth+col];
End;
procedure TMap.SetMap(col,row:Integer;T:TTile);
Begin
If (col<0)or(col>=FWidth)or(row<0)or(row>=FHeight) Then Exit;
FMap[row*FWidth+col]:=T;
End;
function TMap.GetMapStatus(col,row:Integer):byte;
Begin
If (col<0)or(col>=FWidth)or(row<0)or(row>=FHeight) Then Result := tsUnvisited
Else Result := FMapPro[row*FWidth+col].status;
End;
procedure TMap.SetMapStatus(col,row:Integer;T:byte);
Begin
If (col<0)or(col>=FWidth)or(row<0)or(row>=FHeight) Then Exit;
FMapPro[row*FWidth+col].Status:=T;
End;
Function TMap.FindMin: integer;
var i,n: integer;
Ti,Tn : TPoint;
Begin
n:=0;
Tn := FBound[n];
For i:=0 To FBSize-1 Do Begin
Ti := FBound[i];
If FMapPro[Tn.X+Tn.Y*FWidth].fval>FMapPro[Ti.X+Ti.Y*FWidth].fval Then Begin
n:=i; Tn := FBound[n];
End;
End;
result:=n;
End;
procedure TMap.AddToBound(Point:TPoint);
Const delta = 100;
Begin
If Length(FBound)<=FBSize Then
SetLength(FBound,FBSize+delta);
FBound[FBSize]:=Point;
Inc(FBSize);
End;
procedure TMap.Clear;
Var i,j,pos:Integer;
Begin
For j:=0 to FHeight-1 do begin
pos := j*FWidth;
For i:=0 To FWidth-1 do Begin
FMap [pos+i].TerrType := ttClear;
FMap [pos+i].value := 1;
FMapPro[pos+i].Status := tsUnvisited;
End;
End;
End;
// стена по крайним клеткам поля
procedure TMap.Wall;
Var i,j:Integer;
Begin
For i:=0 To FWidth-1 do Begin
// top line
FMap[i].TerrType := ttWall;
FMap[i].value := 1;
// bottom line
FMap[(FHeight-1)*FWidth+i].TerrType := ttWall;
FMap[(FHeight-1)*FWidth+i].value := 1;
If (i=0)or(i=FWidth-1) Then
For j:=0 to FHeight-1 do begin
FMap[j*FWidth+i].TerrType := ttWall;
FMap[j*FWidth+i].value := 1;
End;
End;
End;
// отметить непосещенными все точки
procedure TMap.Unvisit;
Var i,j,pos:Integer;
Begin
For j:=0 to FHeight-1 do begin
pos := j*FWidth;
For i:=0 To FWidth-1 do Begin
FMapPro[pos+i].Status := tsUnvisited;
FMapPro[pos+i].Prev := NullPoint;
End;
End;
End;
Function EqualPoints(a,b:TPoint):boolean ;
Begin
Result:=(A.X=B.X) and (A.Y=B.Y);
End;
Function TMap.GoodPoint(a:TPoint):boolean ;
Begin
Result:=(A.X>=0)and(A.X<FWidth) and (A.Y>=0)and(A.Y<FHeight);
End;
Function Point(AX , AY : integer): TPoint;
Begin
Result.X:=AX;Result.Y:=AY;
End;
Function HEst(A,B: TPoint; dx2,dy2:Double): Double;
var dx,dy,cross : Double;
Begin
dx:= A.X - B.X;
dy:= A.Y - B.Y;
// dx2:= Src.X - Dst.X;
// dy2:= Src.Y - Dst.Y;
cross:= dx*dy2 - dx2*dy;
If( cross<0 ) Then cross:=-cross;
Result:= max(abs(dx), abs(dy))+cross*0.001;
// Result:=sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));
End;
Function TMap.FindPath(Src,Dst: TPoint): boolean;
Label __Ok, __Exi;
const kk : array[0..1] of Double=(1.42,1);
var
A : TPoint;
i,j,k : Integer;
dx,dy : Double;
Tile : TTileProt;
pos : Integer;
Begin
Time := GetTickCount;
Result := False;
If not GoodPoint(Dst) Then Begin ErrorStr := 'Dest point outside map'; Goto __Exi;End;// точкa финиша
If not GoodPoint(Src) Then Begin ErrorStr := 'Src point outside map'; Goto __Exi;End;// точкa старта
If EqualPoints(Src,Dst) Then Begin ErrorStr := 'Src and Dst points are equal';Goto __Exi;End;// точки старта и финиша одна и та же
If FMap[Dst.X+Dst.Y*FWidth].TerrType <> ttClear Then Begin ErrorStr := 'Dst point not on free cell';Goto __Exi;End;
If FMap[Src.X+Src.Y*FWidth].TerrType <> ttClear Then Begin ErrorStr := 'Src point not on free cell';Goto __Exi;End;
FSrc := Src; FDst := Dst;
Unvisit;// обнулить рабочие массивы
dx:=Src.X-Dst.X;
dy:=Src.Y-Dst.Y;
FMapPro[Dst.X+Dst.Y*FWidth].Status := tsFinish;// точка финиша
// точка старта
FMapPro[Src.X+Src.Y*FWidth].Status :=tsBound;
FMapPro[Src.X+Src.Y*FWidth].gval :=0;
FMapPro[Src.X+Src.Y*FWidth].fval :=0+HEst(Src,Dst,dx,dy);
FBSize := 0;
AddToBound(Src);// добавить к границе точку старта
Result:=False;
While FBSize>0 Do Begin
k:=FindMin;
i:=FBound[k].x;
j:=FBound[k].y;
FMapPro[i+j*FWidth].Status := tsPassed;// точка пройдена
FBound[k]:=FBound[FBSize-1];// удалить из границы обработанную точку
Dec(FBSize);
For k:=1 To 8 Do Begin
A := Courses[k];
A.X:=A.X +i;
A.Y:=A.Y +j;
If (a.x>=0)and(a.x<FWidth) and (a.y>=0)and(a.Y<FHeight)and // проверка на выход за границу
(Map[A.x,A.y].TerrType<>ttWall) Then Begin
pos := A.x+A.y*FWidth;// положение в массиве (для ускорения)
Case FMapPro[pos].Status of
tsUnvisited:Begin
Tile.gval :=FMapPro[i+j*FWidth].gval+FMap[pos].value*kk[k mod 2];
Tile.fval :=Tile.gval+HEst(A,Dst,dx,dy);
Tile.Prev :=Point(i,j);
Tile.Status := tsBound;
FMapPro[pos]:=Tile;
AddToBound(A);
End;
tsFinish :Begin
FMapPro[pos].Prev := Point(i,j);
MapStatus[Src.X,Src.Y]:=tsStart;
Result:=True;ErrorStr:='Path found';
Goto __OK;
End;
End;//Case
End;//If
End;//For
End;//While
MapStatus[Src.X,Src.Y]:=tSStart;
ErrorStr := 'Map filled, no path ';
__Ok:
// обратный проход
If Result Then Begin
PathLength := 0;
A:=FMapPro[Dst.X+Dst.Y*FWidth].Prev;
MapStatus[A.x,A.y]:=tsPassed;
While (not EqualPoints(A,Src)) do begin
inc(PathLength);
if MapStatus[A.X,A.y]<>tsBound then MapStatus[A.X,A.y]:=tsPath;
If EqualPoints(a,FMapPro[A.x+A.y*FWidth].Prev) Then Begin// следующая точка равна текущей
Result := False;ErrorStr:='No Path, cycle';Break;
End;
A:=FMapPro[A.x+A.y*FWidth].Prev;
end;
End;
__Exi:
Time := GetTickCount-Time;
End;
Function TMap.SaveToFile(const name:String):Boolean;
Var f : file;
i : Integer;
Begin
Result := False;
If name='' then Exit;
AssignFile(f,name);
ReWrite(f,1);
FHead.Width := fWidth;FHead.Height:=FHeight;
BlockWrite(f,FHead,sizeOf(THead));
For i:=0 To FWidth*FHeight-1 Do Begin
BlockWrite(f,FMap[i],SizeOf(TTile));
End;
CloseFile(f);
Result := True;
End;
Function TMap.LoadFromFile(const name:String):Boolean;
Var f : file;
i : Integer;
Begin
Result := False;
If not FileExists(name) then Exit;
AssignFile(f,name);FileMode := fmOpenRead;
Reset(f,1);
BlockRead(f,FHead,sizeOf(THead));
Fwidth:=FHead.Width;FHeight := FHead.Height;
SetLength(FMap, FHeight*FWidth);
SetLength(FMapPro,FHeight*FWidth);
For i:=0 To FWidth*FHeight-1 Do Begin
BlockRead(f,FMap[i],SizeOf(TTile));
End;
CloseFile(f);
Result := True;
End;
End.
THE COPYRIGHT of THIS DOCUMENT IS THE PROPERTY OF Victor Nekrasov.
All rights reserved. No part of this documentation may be reproduced by any means в any material form
(including photocopying or storing it в any electronic form) without the consent of the Copyright Owner or under the terms of a licence and/or
confidentiality agreement issued by the Copyright Owner, Victor Nekrasov. Applications for the
copyright owners permission to reproduce any part of this documentation should be addressed to, Victor Nekrasov, vinek@list.ru
© 1998-2023 Victor Nekrasov