4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file tilearea.cpp Handling of tile areas. */
14 #include "tilearea_type.h"
16 #include "safeguards.h"
19 * Construct this tile area based on two points.
20 * @param start the start of the area
21 * @param end the end of the area
23 OrthogonalTileArea::OrthogonalTileArea(TileIndex start
, TileIndex end
)
25 assert(start
< MapSize());
26 assert(end
< MapSize());
28 uint sx
= TileX(start
);
29 uint sy
= TileY(start
);
33 if (sx
> ex
) Swap(sx
, ex
);
34 if (sy
> ey
) Swap(sy
, ey
);
36 this->tile
= TileXY(sx
, sy
);
37 this->w
= ex
- sx
+ 1;
38 this->h
= ey
- sy
+ 1;
42 * Add a single tile to a tile area; enlarge if needed.
43 * @param to_add The tile to add
45 void OrthogonalTileArea::Add(TileIndex to_add
)
47 if (this->tile
== INVALID_TILE
) {
54 uint sx
= TileX(this->tile
);
55 uint sy
= TileY(this->tile
);
56 uint ex
= sx
+ this->w
- 1;
57 uint ey
= sy
+ this->h
- 1;
59 uint ax
= TileX(to_add
);
60 uint ay
= TileY(to_add
);
67 this->tile
= TileXY(sx
, sy
);
68 this->w
= ex
- sx
+ 1;
69 this->h
= ey
- sy
+ 1;
73 * Does this tile area intersect with another?
74 * @param ta the other tile area to check against.
75 * @return true if they intersect.
77 bool OrthogonalTileArea::Intersects(const OrthogonalTileArea
&ta
) const
79 if (ta
.w
== 0 || this->w
== 0) return false;
81 assert(ta
.w
!= 0 && ta
.h
!= 0 && this->w
!= 0 && this->h
!= 0);
83 uint left1
= TileX(this->tile
);
84 uint top1
= TileY(this->tile
);
85 uint right1
= left1
+ this->w
- 1;
86 uint bottom1
= top1
+ this->h
- 1;
88 uint left2
= TileX(ta
.tile
);
89 uint top2
= TileY(ta
.tile
);
90 uint right2
= left2
+ ta
.w
- 1;
91 uint bottom2
= top2
+ ta
.h
- 1;
102 * Does this tile area contain a tile?
103 * @param tile Tile to test for.
104 * @return True if the tile is inside the area.
106 bool OrthogonalTileArea::Contains(TileIndex tile
) const
108 if (this->w
== 0) return false;
110 assert(this->w
!= 0 && this->h
!= 0);
112 uint left
= TileX(this->tile
);
113 uint top
= TileY(this->tile
);
114 uint tile_x
= TileX(tile
);
115 uint tile_y
= TileY(tile
);
117 return IsInsideBS(tile_x
, left
, this->w
) && IsInsideBS(tile_y
, top
, this->h
);
121 * Clamp the tile area to map borders.
123 void OrthogonalTileArea::ClampToMap()
125 assert(this->tile
< MapSize());
126 this->w
= min(this->w
, MapSizeX() - TileX(this->tile
));
127 this->h
= min(this->h
, MapSizeY() - TileY(this->tile
));
131 * Create a diagonal tile area from two corners.
132 * @param start First corner of the area.
133 * @param end Second corner of the area.
135 DiagonalTileArea::DiagonalTileArea(TileIndex start
, TileIndex end
) : tile(start
)
137 assert(start
< MapSize());
138 assert(end
< MapSize());
140 /* Unfortunately we can't find a new base and make all a and b positive because
141 * the new base might be a "flattened" corner where there actually is no single
142 * tile. If we try anyway the result is either inaccurate ("one off" half of the
143 * time) or the code gets much more complex;
145 * We also need to increment/decrement a and b here to have one-past-end semantics
146 * for a and b, just the way the orthogonal tile area does it for w and h. */
148 this->a
= TileY(end
) + TileX(end
) - TileY(start
) - TileX(start
);
149 this->b
= TileY(end
) - TileX(end
) - TileY(start
) + TileX(start
);
164 * Does this tile area contain a tile?
165 * @param tile Tile to test for.
166 * @return True if the tile is inside the area.
168 bool DiagonalTileArea::Contains(TileIndex tile
) const
170 int a
= TileY(tile
) + TileX(tile
);
171 int b
= TileY(tile
) - TileX(tile
);
173 int start_a
= TileY(this->tile
) + TileX(this->tile
);
174 int start_b
= TileY(this->tile
) - TileX(this->tile
);
176 int end_a
= start_a
+ this->a
;
177 int end_b
= start_b
+ this->b
;
179 /* Swap if necessary, preserving the "one past end" semantics. */
180 if (start_a
> end_a
) {
185 if (start_b
> end_b
) {
191 return (a
>= start_a
&& a
< end_a
&& b
>= start_b
&& b
< end_b
);
195 * Move ourselves to the next tile in the rectangle on the map.
197 TileIterator
&DiagonalTileIterator::operator++()
199 assert(this->tile
!= INVALID_TILE
);
201 /* Determine the next tile, while clipping at map borders */
202 bool new_line
= false;
204 /* Iterate using the rotated coordinates. */
205 if (this->a_max
== 1 || this->a_max
== -1) {
206 /* Special case: Every second column has zero length, skip them completely */
208 if (this->b_max
> 0) {
209 this->b_cur
= min(this->b_cur
+ 2, this->b_max
);
211 this->b_cur
= max(this->b_cur
- 2, this->b_max
);
214 /* Every column has at least one tile to process */
215 if (this->a_max
> 0) {
217 new_line
= this->a_cur
>= this->a_max
;
220 new_line
= this->a_cur
<= this->a_max
;
223 /* offset of initial a_cur: one tile in the same direction as a_max
226 this->a_cur
= abs(this->a_cur
) % 2 ? 0 : (this->a_max
> 0 ? 1 : -1);
228 if (this->b_max
> 0) {
236 /* And convert the coordinates back once we've gone to the next tile. */
237 uint x
= this->base_x
+ (this->a_cur
- this->b_cur
) / 2;
238 uint y
= this->base_y
+ (this->b_cur
+ this->a_cur
) / 2;
239 /* Prevent wrapping around the map's borders. */
240 this->tile
= x
>= MapSizeX() || y
>= MapSizeY() ? INVALID_TILE
: TileXY(x
, y
);
241 } while (this->tile
> MapSize() && this->b_max
!= this->b_cur
);
243 if (this->b_max
== this->b_cur
) this->tile
= INVALID_TILE
;