Recent

Author Topic: Using Bresenham's line algorithm to plot a path to a target in a matrix  (Read 1018 times)

TBMan

  • Full Member
  • ***
  • Posts: 114
When I started on my clone of Missile Command, my first "how do they do that" moment came when I wanted to draw the enemy missiles. I knew I had to draw a line from the origin point to one of the cities or missile bases, but I had to do it a point at a time while animating everything else. What I did was to "unroll" Bresenham's algorithm by creating a record that held all of the variables used in the algorithm so they can be carried over to the next animation cycle.  Each enemy missile had it's own "line" of attack.

So now I am working on a 2d "dungeon" type game where my "monsters" are randomly going around the map and if they get within a matrix square of the "hero" they attack.  Which is fine. There are a lot of obstacles and going randomly actually works a bit. If I had a line of sight mechanism that I could use it would be helpful, so I might squeeze in what I did here.

I also want to do a tank/battle game similar to the old PC games Empire and The Perfect General. Again, line of sight would be fine here as well. So I did this code below today and I think it is a good start. I'm thinking my enemy "tanks" can have a range of let's say 5-7 rows/columns in any direction so if it finds a valid target, it can fire it's cannon. Doing a quick "line" to each position in the matrix around the tank outside of its immediate neighboring squares I think would work.  The demo below adjusts the ending position to the one prior to the target for movement to the target. Removing this adjustment would give me the target for the firing of the tank's cannon if it is in range. If the target found is out of range, but found within a "radar" range, the tank could move towards it. In the code below it is possible to hide the target from line of sight to ward off the algorithm.

If anyone can point me to a routine that rotates a rectangle in 2d space that works, please let me know. I want to be able to rotate as I think it might work out better than to do extra sprites, but that could work also.

This is what retired guys do if they have a lot of time on their hands, lol.


Code: Pascal  [Select][+][-]
  1. program pathfinder;
  2.  
  3. uses
  4.   ptccrt,  ptcgraph;
  5.  
  6. const
  7.     Maxrows = 15;
  8.     MaxColumns = 20;
  9.     PixelDepth = 32;
  10.     PixelWidth = 32;
  11.     StartX = 0;
  12.     startY = 0;
  13.     Target = 1;
  14.  
  15.  
  16. Type
  17.    MapType = array[0..maxrows,0..maxcolumns] of byte;
  18.  
  19. // the x1,y1,x2,y2 and x,y are used for matrix positions, not pixel positions for this demo
  20.  
  21.  LineT = record
  22.   x1,x2,y1,y2,
  23.   dx, dy, sx, sy, err,
  24.   x, y: integer;
  25.   j:integer;
  26. end;
  27.  
  28. var
  29.   Mx,My,
  30.   SC,SR,
  31.   TC,TR,
  32.   rr,cc: Integer;
  33.   Map:MapType;
  34.   Path:LineT;
  35.   gmode,gdriver:smallint;
  36.  
  37. Procedure PaintBlock(M:MapType;r,c:integer);
  38. var
  39.   x,y:integer;
  40. begin
  41. setfillstyle(solidfill,M[r,c]);
  42. x := c*pixelwidth-startx;
  43. y := r*pixeldepth-starty;
  44. bar(x,y,x+pixelwidth,y+pixeldepth);
  45. rectangle(x,y,x+pixelwidth,y+pixeldepth);
  46. end;
  47.  
  48. {******************************************************************}
  49. {  The "unrolling" of Brensenham's line algorithm starts with this }
  50.  
  51. Procedure SetPath(var LineInfo:LineT;SSY,SSX,EY,EX:integer);
  52. begin
  53. with LineInfo do
  54. begin
  55.   x1 := SSX; Y1 := SSY;
  56.   X2 := EX; Y2 := EY;
  57.  
  58.   X := X1;     // starting point
  59.   Y := Y1;
  60.   dx := abs(x2 - x1);
  61.   dy := abs(y2 - y1);
  62.  
  63.  if x1 < x2 then sx := 1 else sx := -1;
  64.  if y1 < y2 then sy := 1 else sy := -1;
  65.  err := dx - dy;
  66. end;
  67.  
  68. end;
  69.  
  70. Procedure FindNextLocation(Var LineInfo:LineT);
  71.   begin
  72.  
  73.   with LineInfo do
  74.   begin
  75.     if (x <> x2) or (y <> y2) then
  76.     begin
  77.       if (err > 0) then
  78.       begin
  79.         x := x + sx;
  80.         err := err - dy;
  81.       end
  82.       else
  83.       begin
  84.         y := y + sy;
  85.         err := err + dx;
  86.       end;
  87.     end;
  88.   end;
  89.  
  90.  // x and y have the next location in the line
  91.  // which can be out of range in the matrix so it has to be adjusted if that happens
  92.   end;
  93.  
  94. { *********** end of Bresenham's work ******************************}
  95.  
  96. // if true returns block right before target obstacle
  97. Function PathFound(M:MapType;P:LineT;T:integer;var MatrixRow,MatrixColumn:integer):boolean;
  98. var
  99.   found,ClearPath:boolean;
  100. begin
  101.     FindNextLocation(p);
  102.   repeat
  103.      found := false;
  104.      FindNextLocation(p);
  105.      ClearPath := true;
  106.     if (P.x <0) or (P.X > MaxColumns) or
  107.       (P.Y <0) or (P.Y>maxcolumns) then
  108.        ClearPath := false;        // out of matrix range has occurred
  109.     if ClearPath then
  110.     begin
  111.       if m[p.y,p.x] <> 0 then ClearPath := false;  // obstacle found
  112.        if Not Clearpath and
  113.         (m[p.y,p.x] = T) then found := true;     // if it's the target then good
  114.     end;
  115.   until found or not ClearPath;
  116.   MatrixColumn := -1;     // if not a clear path then invalid row, invalid column returned
  117.   MatrixRow := -1;
  118. if found then
  119.  begin
  120.    // if the target was found, back up the matrix position to before target
  121.    with path do
  122.      if err>0 then x := x -sx else y := y-sy;
  123.    MatrixColumn := p.x;
  124.    MatrixRow := p.y;
  125.  end;
  126. result := found;
  127. end;
  128.  
  129.  
  130. begin
  131.   gdriver := vesa;
  132.   Gmode := installusermode(800, 600, 256, 1, 10000, 8000);
  133.   randomize;
  134.   WindowTitle := 'Path finder';
  135.   initgraph(gdriver, gmode, '');
  136.  
  137.   fillchar(map,sizeof(map),0);
  138.   Tc := maxcolumns-2; Tr := 10;  // target column, target row
  139.  
  140.   Sr := 1; Sc := 1;   // start row, start column
  141.   map[Sr,Sc] := 14;
  142.   map[Tr,Tc] := target;
  143.  
  144.   // set up the "line of sight" to the target
  145.   SetPath(Path,Sr,Sc,TR,TC);
  146.   setcolor(7);
  147.  
  148. // put a wall around Target to test - remove comment slashes
  149. // range checking not done here
  150.  
  151. //map[tr-1,tc-1] := 2;
  152. //map[tr-2,tc] := 2;
  153. //map[tr,tc-1] := 2;
  154.  
  155. // paint screen for demo
  156.   for rr := 0 to maxrows do
  157.   begin
  158.     for cc := 0 to maxcolumns do
  159.      begin
  160.         paintblock(map,rr,cc);
  161.      end;
  162.   end;
  163.  
  164.   mx := 0; my := 0;
  165.   Setcolor(15);
  166.   if pathfound(map,path,Target,mx,my) then
  167.     begin
  168.       // my and mx have the location prior to target
  169.       outtextxy(300,getmaxy-80,'Found target');
  170.       // now draw path to target or positions can be stored for movement each animation cycle
  171.       FindNextLocation(Path);
  172.       repeat
  173.          map[path.y,path.x] := 14;
  174.          paintblock(map,path.y,path.x);
  175.          FindNextLocation(Path);       // x and y updated for next position
  176.       until map[path.y,path.x] <> 0;   // until target
  177.     end
  178.   else
  179.   begin
  180.     paintblock(Map,path.y1,path.x1);
  181.     outtextxy(300,getmaxy-80,'Target not found, obstacle or out of matrix range');
  182.   end;
  183.  
  184. readkey;
  185. closegraph;
  186. end.
  187.  
  188.  
  189.  
« Last Edit: March 19, 2025, 03:15:27 pm by TBMan »

Paolo

  • Hero Member
  • *****
  • Posts: 579
To rotate a rectangle you have to:
First traslate the 4 vertices of rectangle in such a way the rotation point becames the origin of coordinate system, then Multiply the vertices for the rotation matrix, and finally traslate back to the rotation pont, thus: if x,y is one vartex and x0,y0 is the point around rotation happens do this:
1) traslate: xnew=x-x0, ynew=y-y0
2) rotate by alfa (radiants):
xrot=xnew*cos(alfa)+ynew*sin(alfa),
yrot=xnew*(-sin(alfa))+ynew*cos(alfa)
4) come back
Xfinal=xrot+x0
Yfinal=yrot+y0.

Repeat this for all the 4 points, then join them with lpines.

Is this you need ?

TBMan

  • Full Member
  • ***
  • Posts: 114
To rotate a rectangle you have to:
First traslate the 4 vertices of rectangle in such a way the rotation point becames the origin of coordinate system, then Multiply the vertices for the rotation matrix, and finally traslate back to the rotation pont, thus: if x,y is one vartex and x0,y0 is the point around rotation happens do this:
1) traslate: xnew=x-x0, ynew=y-y0
2) rotate by alfa (radiants):
xrot=xnew*cos(alfa)+ynew*sin(alfa),
yrot=xnew*(-sin(alfa))+ynew*cos(alfa)
4) come back
Xfinal=xrot+x0
Yfinal=yrot+y0.

Repeat this for all the 4 points, then join them with lpines.

Is this you need ?

I was using that and the sources always said to use the center of the rectangle, but the results I got weren't good. I just reviewed it and saw my error, thanks for the info!
« Last Edit: March 20, 2025, 02:05:36 am by TBMan »

speter

  • Sr. Member
  • ****
  • Posts: 366
For entertainment, have a look at the attached project.

It shows a "rectangle" that rotates to "follow" the mouse. :)

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

TBMan

  • Full Member
  • ***
  • Posts: 114
Speaking of rotating rectangles, here's a demo of my new rotating skills, lol.

Now, if I can take the unrolled Bresenham line algorithm I showed in my OP and use it to plot from (x.0,y.0 downto x.2,y.2) and then (x.1,y.1 downto x.3,y.3) there is probably a way to map a bitmap of an array of bytes going across with yet another plotted line using the x,y as bitmap matrix cells.

I bet that's in there someplace.


Code: Pascal  [Select][+][-]
  1.        program play7;
  2. // good rotation demo
  3.  
  4. uses
  5.   ptccrt,
  6.   ptcgraph,
  7.   Math;
  8.  
  9. type
  10.  
  11.   TSinglePoint = record
  12.     x, y: single;
  13.   end;
  14.  
  15.   Arect = record
  16.     c: array[0..3] of TsinglePoint;
  17.     Center: tsinglePoint;
  18.   end;
  19.  
  20. var
  21.   gmode, gdriver: smallint;
  22.   ab: integer;
  23.   Rect, rect2,rect3: Arect;
  24.  
  25.  
  26.   procedure SetARect(var r: arect; Xx, yy, w, d: integer);
  27.   begin
  28.     r.c[0].x := xx;    // top left
  29.     r.c[0].y := yy;
  30.     r.c[1].x := xx + w;  //top right
  31.     r.c[1].y := yy;
  32.  
  33.     r.c[2].x := xx;    // bottom left
  34.     r.c[2].y := yy + d;
  35.  
  36.     r.c[3].x := xx + w;  //bottom right
  37.     r.c[3].y := yy + d;
  38.  
  39.     r.center.x := (r.c[1].x - r.c[0].x) / 2 + r.c[0].x;
  40.     r.center.y := (r.c[2].y - r.c[0].x) / 2 + r.c[0].y;
  41.   end;
  42.  
  43.   procedure DrawRect(R: arect; color: integer);
  44.   begin
  45.     setcolor(color);
  46.  
  47.     line(round(r.c[0].x), round(r.c[0].y), round(r.c[1].x), round(r.c[1].y));
  48.     line(round(r.c[0].x), round(r.c[0].y), round(r.c[2].x), round(r.c[2].y));
  49.     line(round(r.c[2].x), round(r.c[2].y), round(r.c[3].x), round(r.c[3].y));
  50.     line(round(r.c[3].x), round(r.c[3].y), round(r.c[1].x), round(r.c[1].y));
  51.   end;
  52.  
  53.   procedure rotateRect(var R: arect; degrees: float);
  54.   var
  55.     rr: integer;
  56.     a: arect;
  57.     w, d, newx, newy, angle: float;
  58.     cx, cy, y, x, x1, y1, x2, y2: single;
  59.   begin
  60.     angle := degTorad(degrees);
  61.  
  62.     cx := r.center.x;
  63.     cy := r.center.y;
  64.     for rr := 0 to 3 do
  65.     begin
  66.       x := r.c[rr].x;
  67.       y := r.c[rr].y;
  68.       x := x - cx;
  69.       Y := y - cy;
  70.       newx := (x * cos(angle)) - (y * sin(angle));
  71.       newy := (x * sin(angle)) + (y * cos(angle));
  72.       newx := newx + cx;
  73.       newy := newy + cy;
  74.       a.c[rr].x := newx;
  75.       a.c[rr].y := newy;
  76.     end;
  77.     a.center.x := r.center.x;
  78.     a.center.y := r.center.y;
  79.     move(a, r, sizeof(a));
  80.   end;
  81.  
  82.  
  83. begin
  84.   clrscr;
  85.   gdriver := vesa;
  86.   Gmode := installusermode(1000, 800, 256, 1, 10000, 8000);
  87.   randomize;
  88.   WindowTitle := 'Rotation';
  89.   initgraph(gdriver, gmode, '');
  90.   // x,y, width, depth
  91.   SetArect(Rect, 300, 300, 60, 60);
  92.   SetARect(Rect2, 500, 400, 300, 225);
  93.   SetArect(rect3,200,210,80,80);
  94.   Set Write mode (xorput);// take out the spaces between the words
  95.  repeat
  96.     rotaterect(rect, 1);
  97.     rotaterect(rect2, 1);
  98.     rotaterect(rect3,1);
  99.     drawrect(rect, 15);
  100.     drawrect(rect2, 14);
  101.     drawrect(rect3,2);
  102.     delay(10);
  103.     drawrect(rect, 15);
  104.     drawrect(rect2, 14);
  105.     drawrect(rect3,2);
  106. until keypressed;
  107.  
  108.  
  109.   closegraph;
  110. end.                    
  111.  
« Last Edit: March 23, 2025, 01:45:39 am by TBMan »

Paolo

  • Hero Member
  • *****
  • Posts: 579
Ciao TBMan,

some notes:

why

Code: Pascal  [Select][+][-]
  1. move(a, r, sizeof(a));
  2.  

insted of

Code: Pascal  [Select][+][-]
  1. r:=a;
  2.  

and if you do this

Code: Pascal  [Select][+][-]
  1. ...
  2.   newx := (x * cos(angle)) - (y * sin(angle));
  3.   newy := (x * sin(angle)) + (y * cos(angle));
  4. ...
  5.  

it could be better do

Code: Pascal  [Select][+][-]
  1. var
  2.   Sn, Cs : double;
  3. ...
  4.   SinCos(Angle, Sn, Cs)
  5.    newx := x * Cs - y * Sn;
  6.    newy := x * Sn + y * Cs;
  7.  

then your code code has some lines corrupted (At least is what I see)

Code: Pascal  [Select][+][-]
  1.   SetArect(Rect, 300, 300, 60, 60);
  2.   SetARect(Rect2, 500, 400, 300, 225);
  3.  
  4.   setwri]"]>Blockedde(xorput);   <---- here
  5.   for ab := 1 to 90 do
  6.   begin
  7.     rotaterect(rect, 1);
  8.     rotaterect(rect2, 1);
  9.     drawrect(rect, 15);
  10.     drawrect(rect2, 14);
  11.     delay(50);
  12.     drawrect(rect, 15);
  13.     drawrect(rect2, 14);
  14.   end;
  15.   setwri]"]>Blockedde(normalput); <--- here
  16.   drawrect(rect, 15);
  17.   drawrect(rect2, 14);
  18.  
  19.  

and finally, even removing the corrupted lines the app gives an error message at line below, but then starts, any idea why this behaviour)

Code: Pascal  [Select][+][-]
  1.   initgraph(gdriver, gmode, '');
  2.  

« Last Edit: March 22, 2025, 08:02:50 pm by Paolo »

TBMan

  • Full Member
  • ***
  • Posts: 114
There's a bug in the forum as I have edited and replaced that set_write_mode line several times and the forum keeps changing it.
As far as the error for initgraph, maybe changing the gmode=installusermode to a lower resolution or getting rid of it entirely.

I'm an old time coder and back when I learned Turbo Pascal (late 1980s) I don't think you could copy arrays or records by saying
Code: Pascal  [Select][+][-]
  1. a:=r;
« Last Edit: March 23, 2025, 02:08:15 pm by TBMan »

cdbc

  • Hero Member
  • *****
  • Posts: 2078
    • http://www.cdbc.dk
Hi
The forum blocks t_e_m_o & ttt_eee_mmm_uuu!!! They are  blocked fiercely!
Cannot write without _ & the last one not even with!!!
Regards Benny
« Last Edit: March 23, 2025, 07:28:49 am by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

TBMan

  • Full Member
  • ***
  • Posts: 114
Hi
The forum blocks t_e_m_o & ttt_eee_mmm_uuu!!! They are  blocked fiercely!
Cannot write without _ & the last one not even with!!!
Regards Benny

Why does it block those letter combinations?

cdbc

  • Hero Member
  • *****
  • Posts: 2078
    • http://www.cdbc.dk
Hi
Well, apparently them chinamen's adds and promotions are highly intrusive and disrespectful regarding website rules and contenance...
It's a biiig online marketplace...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

TBMan

  • Full Member
  • ***
  • Posts: 114
Re: Using Bresenham's line algorithm to plot a path to a target in a matrix
« Reply #10 on: March 23, 2025, 03:37:47 pm »
Hi
Well, apparently them chinamen's adds and promotions are highly intrusive and disrespectful regarding website rules and contenance...
It's a biiig online marketplace...
Regards Benny

LOL. I searched for t_e_m_o on Amazon and now I see what you mean.

 

TinyPortal © 2005-2018