2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file map/tilearea.cpp Handling of tile areas. */
10 #include "../stdafx.h"
15 * Set this tile area based on initial and final coordinates.
16 * @param x0 initial x of the area
17 * @param y0 initial y of the area
18 * @param x1 final x of the area
19 * @param y1 final y of the area
21 inline void OrthogonalTileArea::Set(uint x0
, uint y0
, uint x1
, uint y1
)
23 this->tile
= TileXY(x0
, y0
);
24 this->w
= x1
- x0
+ 1;
25 this->h
= y1
- y0
+ 1;
29 * Construct this tile area based on two points.
30 * @param start the start of the area
31 * @param end the end of the area
33 OrthogonalTileArea::OrthogonalTileArea(TileIndex start
, TileIndex end
)
35 uint sx
= TileX(start
);
36 uint sy
= TileY(start
);
40 if (sx
> ex
) Swap(sx
, ex
);
41 if (sy
> ey
) Swap(sy
, ey
);
43 this->Set(sx
, sy
, ex
, ey
);
47 * Add a single tile to a tile area; enlarge if needed.
48 * @param to_add The tile to add
50 void OrthogonalTileArea::Add(TileIndex to_add
)
52 assert (to_add
!= INVALID_TILE
);
61 uint sx
= TileX(this->tile
);
62 uint sy
= TileY(this->tile
);
63 uint ex
= sx
+ this->w
- 1;
64 uint ey
= sy
+ this->h
- 1;
66 uint ax
= TileX(to_add
);
67 uint ay
= TileY(to_add
);
69 this->Set(min(ax
, sx
), min(ay
, sy
), max(ax
, ex
), max(ay
, ey
));
73 * Add another tile area to a tile area; enlarge if needed.
74 * @param to_add The tile area to add
76 void OrthogonalTileArea::Add(const OrthogonalTileArea
&to_add
)
78 if (to_add
.empty()) return;
81 this->tile
= to_add
.tile
;
87 uint sx
= TileX(this->tile
);
88 uint sy
= TileY(this->tile
);
89 uint ex
= sx
+ this->w
- 1;
90 uint ey
= sy
+ this->h
- 1;
92 uint ax
= TileX(to_add
.tile
);
93 uint ay
= TileY(to_add
.tile
);
94 uint zx
= ax
+ to_add
.w
- 1;
95 uint zy
= ay
+ to_add
.h
- 1;
97 this->Set(min(ax
, sx
), min(ay
, sy
), max(zx
, ex
), max(zy
, ey
));
101 * Does this tile area intersect with another?
102 * @param ta the other tile area to check against.
103 * @return true if they intersect.
105 bool OrthogonalTileArea::Intersects(const OrthogonalTileArea
&ta
) const
107 if (ta
.w
== 0 || this->w
== 0) return false;
109 assert(ta
.w
!= 0 && ta
.h
!= 0 && this->w
!= 0 && this->h
!= 0);
111 uint left1
= TileX(this->tile
);
112 uint top1
= TileY(this->tile
);
113 uint right1
= left1
+ this->w
- 1;
114 uint bottom1
= top1
+ this->h
- 1;
116 uint left2
= TileX(ta
.tile
);
117 uint top2
= TileY(ta
.tile
);
118 uint right2
= left2
+ ta
.w
- 1;
119 uint bottom2
= top2
+ ta
.h
- 1;
130 * Does this tile area contain a tile?
131 * @param tile Tile to test for.
132 * @return True if the tile is inside the area.
134 bool OrthogonalTileArea::Contains(TileIndex tile
) const
136 if (this->w
== 0) return false;
138 assert(this->w
!= 0 && this->h
!= 0);
140 uint left
= TileX(this->tile
);
141 uint top
= TileY(this->tile
);
142 uint tile_x
= TileX(tile
);
143 uint tile_y
= TileY(tile
);
145 return IsInsideBS(tile_x
, left
, this->w
) && IsInsideBS(tile_y
, top
, this->h
);
149 * Clamp the tile area to map borders.
151 void OrthogonalTileArea::ClampToMap()
153 assert(this->tile
< MapSize());
154 this->w
= min(this->w
, MapSizeX() - TileX(this->tile
));
155 this->h
= min(this->h
, MapSizeY() - TileY(this->tile
));
158 /** Expand the area by a given amount. */
159 void OrthogonalTileArea::expand (uint radius
)
161 uint x
= TileX(this->tile
);
163 this->w
= min (this->w
+ x
+ radius
, MapSizeX());
167 this->w
= min (this->w
+ 2 * radius
, MapSizeX() - x
);
170 uint y
= TileY(this->tile
);
172 this->h
= min (this->h
+ y
+ radius
, MapSizeY());
176 this->h
= min (this->h
+ 2 * radius
, MapSizeY() - y
);
179 this->tile
= TileXY (x
, y
);
182 /** Expand the area by a given amount in all directions. */
183 void OrthogonalTileArea::expand (uint xm
, uint ym
, uint xp
, uint yp
)
185 uint x
= TileX(this->tile
);
187 this->w
= min (this->w
+ x
+ xp
, MapSizeX());
191 this->w
= min (this->w
+ xm
+ xp
, MapSizeX() - x
);
194 uint y
= TileY(this->tile
);
196 this->h
= min (this->h
+ y
+ yp
, MapSizeY());
200 this->h
= min (this->h
+ ym
+ yp
, MapSizeY() - y
);
203 this->tile
= TileXY (x
, y
);
208 * Construct this tile area based on two points.
209 * @param start the start of the area
210 * @param end the end of the area
212 DiagonalTileArea::DiagonalTileArea (TileIndex start
, TileIndex end
)
214 uint sx
= TileX(start
);
215 uint sy
= TileY(start
);
219 uint ex
= TileX(end
);
220 uint ey
= TileY(end
);
224 if (sa
> ea
) Swap(sa
, ea
);
225 if (sb
> eb
) Swap(sb
, eb
);
234 * Does this tile area contain a tile?
235 * @param tile Tile to test for.
236 * @return True if the tile is inside the area.
238 bool DiagonalTileArea::Contains (TileIndex tile
) const
240 uint x
= TileX(tile
);
241 uint y
= TileY(tile
);
245 return a
>= a0
&& a
<= a1
&& b
>= b0
&& b
<= b1
;
250 * There are two possibilities for a diagonal iterator: an "even" area or
251 * an "odd" area, where even/odd is determined by the parity of the sum of
252 * differences between the coordinates of the two endpoints.
262 * In our iterator, a "row" will run diagonally (up,right)-wards (so the
263 * first tile will be (0,0), then (1,1), etc.; when we wrap around, we
264 * start the second row at (1,0), then (2,1). As you can see, there are
265 * some differences between an even area and an odd area:
266 * * In an even area, rows have two different lengths, while rows in an
267 * odd area are all of the same length.
268 * * The difference between the last tile in a row and the first one in
269 * the next row is constant in an even area, but not so in an odd area.
270 * * As a particular case, even areas can have no tiles in odd-numbered
271 * rows, if their side has only one tile.
273 * This all leads to the following implementation.
277 * Construct the iterator.
278 * @param corner1 Tile from where to begin iterating.
279 * @param corner2 Tile where to end the iterating.
281 DiagonalTileIterator::DiagonalTileIterator(TileIndex corner1
, TileIndex corner2
)
282 : TileIterator(corner2
), x(TileX(corner2
)), y(TileY(corner2
))
284 assert(corner1
< MapSize());
285 assert(corner2
< MapSize());
287 int dist_x
= TileX(corner1
) - TileX(corner2
);
288 int dist_y
= TileY(corner1
) - TileY(corner2
);
289 int w
= dist_x
+ dist_y
;
290 int h
= dist_y
- dist_x
;
292 this->odd
= (w
% 2) != 0;
294 /* See the ASCII art above to understand these. */
319 } else { /* w == 0 */
320 /* Hack in some values that make things work without a special case in Next. */
321 this->odd
= true; /* true intended */
323 this->s2y
= (h
>= 0) ? 1 : -1;
324 this->s2x
= -this->s2y
;
334 * Move ourselves to the next tile in the rectangle on the map.
336 void DiagonalTileIterator::Next()
338 assert(this->tile
!= INVALID_TILE
);
340 /* Determine the next tile, while clipping at map borders */
343 /* next tile in the currrent row */
347 } else if (this->m
> 0) {
350 this->x
+= this->s2x
;
351 this->y
+= this->s2y
;
353 if ((this->m
% 2) != 0) {
354 /* adjust odd-numbered rows */
356 /* odd area, correct initial tile */
360 /* even area, correct row length */
367 this->tile
= INVALID_TILE
;
370 } while (this->x
>= MapSizeX() || this->y
>= MapSizeY());
372 this->tile
= TileXY(this->x
, this->y
);
377 * Construct an iterator to iterate around a tile area.
378 * @param ta Tile area around which to search.
379 * @param radius Number of circles (rings) to search outwards
381 CircularTileIterator::CircularTileIterator (const TileArea
&ta
, uint radius
)
382 : TileIterator (ta
.tile
+ TileDiffXY (ta
.w
, -1))
387 this->x
= TileX(ta
.tile
) + ta
.w
;
388 this->y
= TileY(ta
.tile
) - 1;
389 this->extent
[0] = ta
.w
+ 1;
390 this->extent
[1] = ta
.h
+ 1;
392 this->d
= DIAGDIR_BEGIN
;
395 if (this->x
>= MapSizeX() || this->y
>= MapSizeY()) this->CircularTileIterator::Next();
399 * Construct an iterator to iterate over a square.
400 * @param tile First tile to search (centre of square)
401 * @param size Length of square size
403 CircularTileIterator::CircularTileIterator (TileIndex tile
, uint size
)
404 : TileIterator (tile
+ 1 - size
% 2)
408 this->x
= TileX(tile
) + 1 - size
% 2;
409 this->y
= TileY(tile
);
410 this->extent
[0] = size
% 2 + 1;
411 this->extent
[1] = size
% 2 + 1;
412 /* use a special marker (j = 0) if the square side length is odd */
413 this->j
= 1 - size
% 2;
414 this->d
= DIAGDIR_BEGIN
;
417 if (this->x
>= MapSizeX() || this->y
>= MapSizeY()) this->CircularTileIterator::Next();
420 /** Get the next tile to iterate over. */
421 void CircularTileIterator::Next()
424 /* special marker for the centre of a square of odd side length */
425 assert(this->extent
[AXIS_X
] == 2);
426 assert(this->extent
[AXIS_Y
] == 2);
427 assert(this->d
== DIAGDIR_BEGIN
);
429 this->x
++; /* move west */
433 if (this->x
< MapSizeX() && this->y
< MapSizeY()) {
434 this->tile
= TileXY(this->x
, this->y
);
440 assert (this->j
!= 0);
442 /* next tile on this side */
443 CoordDiff cdiff
= CoordDiffByDiagDir (this->d
);
447 if (--this->j
== 0) {
449 if (this->d
== DIAGDIR_END
) {
450 if (--this->r
== 0) {
452 this->tile
= INVALID_TILE
;
457 this->x
++; /* move west */
459 this->extent
[AXIS_X
] += 2;
460 this->extent
[AXIS_Y
] += 2;
461 this->d
= DIAGDIR_BEGIN
;
464 /* prepare for next side */
465 this->j
= this->extent
[DiagDirToAxis(this->d
)];
467 } while (this->x
>= MapSizeX() || this->y
>= MapSizeY());
469 this->tile
= TileXY(this->x
, this->y
);