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 tilearea.cpp Handling of tile areas. */
12 #include "tilearea_type.h"
14 #include "safeguards.h"
17 * Construct this tile area based on two points.
18 * @param start the start of the area
19 * @param end the end of the area
21 OrthogonalTileArea::OrthogonalTileArea(TileIndex start
, TileIndex end
)
23 assert(start
< MapSize());
24 assert(end
< MapSize());
26 uint sx
= TileX(start
);
27 uint sy
= TileY(start
);
31 if (sx
> ex
) Swap(sx
, ex
);
32 if (sy
> ey
) Swap(sy
, ey
);
34 this->tile
= TileXY(sx
, sy
);
35 this->w
= ex
- sx
+ 1;
36 this->h
= ey
- sy
+ 1;
40 * Add a single tile to a tile area; enlarge if needed.
41 * @param to_add The tile to add
43 void OrthogonalTileArea::Add(TileIndex to_add
)
45 if (this->tile
== INVALID_TILE
) {
52 uint sx
= TileX(this->tile
);
53 uint sy
= TileY(this->tile
);
54 uint ex
= sx
+ this->w
- 1;
55 uint ey
= sy
+ this->h
- 1;
57 uint ax
= TileX(to_add
);
58 uint ay
= TileY(to_add
);
65 this->tile
= TileXY(sx
, sy
);
66 this->w
= ex
- sx
+ 1;
67 this->h
= ey
- sy
+ 1;
71 * Does this tile area intersect with another?
72 * @param ta the other tile area to check against.
73 * @return true if they intersect.
75 bool OrthogonalTileArea::Intersects(const OrthogonalTileArea
&ta
) const
77 if (ta
.w
== 0 || this->w
== 0) return false;
79 assert(ta
.w
!= 0 && ta
.h
!= 0 && this->w
!= 0 && this->h
!= 0);
81 uint left1
= TileX(this->tile
);
82 uint top1
= TileY(this->tile
);
83 uint right1
= left1
+ this->w
- 1;
84 uint bottom1
= top1
+ this->h
- 1;
86 uint left2
= TileX(ta
.tile
);
87 uint top2
= TileY(ta
.tile
);
88 uint right2
= left2
+ ta
.w
- 1;
89 uint bottom2
= top2
+ ta
.h
- 1;
100 * Does this tile area contain a tile?
101 * @param tile Tile to test for.
102 * @return True if the tile is inside the area.
104 bool OrthogonalTileArea::Contains(TileIndex tile
) const
106 if (this->w
== 0) return false;
108 assert(this->w
!= 0 && this->h
!= 0);
110 uint left
= TileX(this->tile
);
111 uint top
= TileY(this->tile
);
112 uint tile_x
= TileX(tile
);
113 uint tile_y
= TileY(tile
);
115 return IsInsideBS(tile_x
, left
, this->w
) && IsInsideBS(tile_y
, top
, this->h
);
119 * Expand a tile area by rad tiles in each direction, keeping within map bounds.
120 * @param rad Number of tiles to expand
121 * @return The OrthogonalTileArea.
123 OrthogonalTileArea
&OrthogonalTileArea::Expand(int rad
)
125 int x
= TileX(this->tile
);
126 int y
= TileY(this->tile
);
128 int sx
= max(x
- rad
, 0);
129 int sy
= max(y
- rad
, 0);
130 int ex
= min(x
+ this->w
+ rad
, MapSizeX());
131 int ey
= min(y
+ this->h
+ rad
, MapSizeY());
133 this->tile
= TileXY(sx
, sy
);
140 * Clamp the tile area to map borders.
142 void OrthogonalTileArea::ClampToMap()
144 assert(this->tile
< MapSize());
145 this->w
= min(this->w
, MapSizeX() - TileX(this->tile
));
146 this->h
= min(this->h
, MapSizeY() - TileY(this->tile
));
150 * Create a diagonal tile area from two corners.
151 * @param start First corner of the area.
152 * @param end Second corner of the area.
154 DiagonalTileArea::DiagonalTileArea(TileIndex start
, TileIndex end
) : tile(start
)
156 assert(start
< MapSize());
157 assert(end
< MapSize());
159 /* Unfortunately we can't find a new base and make all a and b positive because
160 * the new base might be a "flattened" corner where there actually is no single
161 * tile. If we try anyway the result is either inaccurate ("one off" half of the
162 * time) or the code gets much more complex;
164 * We also need to increment/decrement a and b here to have one-past-end semantics
165 * for a and b, just the way the orthogonal tile area does it for w and h. */
167 this->a
= TileY(end
) + TileX(end
) - TileY(start
) - TileX(start
);
168 this->b
= TileY(end
) - TileX(end
) - TileY(start
) + TileX(start
);
183 * Does this tile area contain a tile?
184 * @param tile Tile to test for.
185 * @return True if the tile is inside the area.
187 bool DiagonalTileArea::Contains(TileIndex tile
) const
189 int a
= TileY(tile
) + TileX(tile
);
190 int b
= TileY(tile
) - TileX(tile
);
192 int start_a
= TileY(this->tile
) + TileX(this->tile
);
193 int start_b
= TileY(this->tile
) - TileX(this->tile
);
195 int end_a
= start_a
+ this->a
;
196 int end_b
= start_b
+ this->b
;
198 /* Swap if necessary, preserving the "one past end" semantics. */
199 if (start_a
> end_a
) {
204 if (start_b
> end_b
) {
210 return (a
>= start_a
&& a
< end_a
&& b
>= start_b
&& b
< end_b
);
214 * Move ourselves to the next tile in the rectangle on the map.
216 TileIterator
&DiagonalTileIterator::operator++()
218 assert(this->tile
!= INVALID_TILE
);
220 /* Determine the next tile, while clipping at map borders */
221 bool new_line
= false;
223 /* Iterate using the rotated coordinates. */
224 if (this->a_max
== 1 || this->a_max
== -1) {
225 /* Special case: Every second column has zero length, skip them completely */
227 if (this->b_max
> 0) {
228 this->b_cur
= min(this->b_cur
+ 2, this->b_max
);
230 this->b_cur
= max(this->b_cur
- 2, this->b_max
);
233 /* Every column has at least one tile to process */
234 if (this->a_max
> 0) {
236 new_line
= this->a_cur
>= this->a_max
;
239 new_line
= this->a_cur
<= this->a_max
;
242 /* offset of initial a_cur: one tile in the same direction as a_max
245 this->a_cur
= abs(this->a_cur
) % 2 ? 0 : (this->a_max
> 0 ? 1 : -1);
247 if (this->b_max
> 0) {
255 /* And convert the coordinates back once we've gone to the next tile. */
256 uint x
= this->base_x
+ (this->a_cur
- this->b_cur
) / 2;
257 uint y
= this->base_y
+ (this->b_cur
+ this->a_cur
) / 2;
258 /* Prevent wrapping around the map's borders. */
259 this->tile
= x
>= MapSizeX() || y
>= MapSizeY() ? INVALID_TILE
: TileXY(x
, y
);
260 } while (this->tile
> MapSize() && this->b_max
!= this->b_cur
);
262 if (this->b_max
== this->b_cur
) this->tile
= INVALID_TILE
;