Translations update
[openttd/fttd.git] / src / map / tilearea.cpp
blob339161af49c5e9b18ff2cd234c1fbf70aeca9c14
1 /*
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/>.
6 */
8 /** @file map/tilearea.cpp Handling of tile areas. */
10 #include "../stdafx.h"
12 #include "tilearea.h"
14 /**
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;
28 /**
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);
37 uint ex = TileX(end);
38 uint ey = TileY(end);
40 if (sx > ex) Swap(sx, ex);
41 if (sy > ey) Swap(sy, ey);
43 this->Set(sx, sy, ex, ey);
46 /**
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);
54 if (this->empty()) {
55 this->tile = to_add;
56 this->w = 1;
57 this->h = 1;
58 return;
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));
72 /**
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;
80 if (this->empty()) {
81 this->tile = to_add.tile;
82 this->w = to_add.w;
83 this->h = to_add.h;
84 return;
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;
121 return !(
122 left2 > right1 ||
123 right2 < left1 ||
124 top2 > bottom1 ||
125 bottom2 < top1
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);
162 if (x < radius) {
163 this->w = min (this->w + x + radius, MapSizeX());
164 x = 0;
165 } else {
166 x -= radius;
167 this->w = min (this->w + 2 * radius, MapSizeX() - x);
170 uint y = TileY(this->tile);
171 if (y < radius) {
172 this->h = min (this->h + y + radius, MapSizeY());
173 y = 0;
174 } else {
175 y -= radius;
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);
186 if (x < xm) {
187 this->w = min (this->w + x + xp, MapSizeX());
188 x = 0;
189 } else {
190 x -= xm;
191 this->w = min (this->w + xm + xp, MapSizeX() - x);
194 uint y = TileY(this->tile);
195 if (y < ym) {
196 this->h = min (this->h + y + yp, MapSizeY());
197 y = 0;
198 } else {
199 y -= ym;
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);
216 int sa = sx + sy;
217 int sb = sx - sy;
219 uint ex = TileX(end);
220 uint ey = TileY(end);
221 int ea = ex + ey;
222 int eb = ex - ey;
224 if (sa > ea) Swap(sa, ea);
225 if (sb > eb) Swap(sb, eb);
227 a0 = sa;
228 a1 = ea;
229 b0 = sb;
230 b1 = 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);
242 int a = x + y;
243 int b = x - y;
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.
254 * x x x
255 * x x x x x x x
256 * x x x x x x x x x
257 * x x x x x x x x x
258 * x x x x x
260 * even odd
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. */
295 if (w > 0) {
296 this->s1 = 1;
297 if (h >= 0) {
298 this->s2x = 0;
299 this->s2y = 1;
300 } else {
301 this->s2x = 1;
302 this->s2y = 0;
304 w = w / 2;
305 this->s2x -= w;
306 this->s2y -= w;
307 } else if (w < 0) {
308 this->s1 = -1;
309 if (h >= 0) {
310 this->s2x = -1;
311 this->s2y = 0;
312 } else {
313 this->s2x = 0;
314 this->s2y = -1;
316 w = (-w) / 2;
317 this->s2x += w;
318 this->s2y += w;
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 */
322 this->s1 = 0;
323 this->s2y = (h >= 0) ? 1 : -1;
324 this->s2x = -this->s2y;
325 h /= 2;
328 this->w = w;
329 this->n = w;
330 this->m = abs(h);
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 */
341 do {
342 if (this->n > 0) {
343 /* next tile in the currrent row */
344 this->n--;
345 this->x += this->s1;
346 this->y += this->s1;
347 } else if (this->m > 0) {
348 /* begin next row */
349 this->m--;
350 this->x += this->s2x;
351 this->y += this->s2y;
352 this->n = this->w;
353 if ((this->m % 2) != 0) {
354 /* adjust odd-numbered rows */
355 if (this->odd ) {
356 /* odd area, correct initial tile */
357 this->x -= this->s1;
358 this->y -= this->s1;
359 } else {
360 /* even area, correct row length */
361 assert(this->n > 0);
362 this->n--;
365 } else {
366 /* all done */
367 this->tile = INVALID_TILE;
368 return;
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))
384 assert(!ta.empty());
385 assert(radius > 0);
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;
391 this->j = ta.w + 1;
392 this->d = DIAGDIR_BEGIN;
393 this->r = radius;
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)
406 assert(size > 1);
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;
415 this->r = size / 2;
417 if (this->x >= MapSizeX() || this->y >= MapSizeY()) this->CircularTileIterator::Next();
420 /** Get the next tile to iterate over. */
421 void CircularTileIterator::Next()
423 if (j == 0) {
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 */
430 this->y--;
431 this->j = 2;
433 if (this->x < MapSizeX() && this->y < MapSizeY()) {
434 this->tile = TileXY(this->x, this->y);
435 return;
439 do {
440 assert (this->j != 0);
442 /* next tile on this side */
443 CoordDiff cdiff = CoordDiffByDiagDir (this->d);
444 this->x += cdiff.x;
445 this->y += cdiff.y;
447 if (--this->j == 0) {
448 this->d++;
449 if (this->d == DIAGDIR_END) {
450 if (--this->r == 0) {
451 /* all done */
452 this->tile = INVALID_TILE;
453 return;
456 /* next circle */
457 this->x++; /* move west */
458 this->y--;
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);