# tilearea.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013
    00014 #include "tilearea_type.h"
    00015
    00021 TileArea::TileArea(TileIndex start, TileIndex end)
    00022 {
    00023   uint sx = TileX(start);
    00024   uint sy = TileY(start);
    00025   uint ex = TileX(end);
    00026   uint ey = TileY(end);
    00027
    00028   if (sx > ex) Swap(sx, ex);
    00029   if (sy > ey) Swap(sy, ey);
    00030
    00031   this->tile = TileXY(sx, sy);
    00032   this->w    = ex - sx + 1;
    00033   this->h    = ey - sy + 1;
    00034 }
    00035
    00040 void TileArea::Add(TileIndex to_add)
    00041 {
    00042   if (this->tile == INVALID_TILE) {
    00043     this->tile = to_add;
    00044     this->w = 1;
    00045     this->h = 1;
    00046     return;
    00047   }
    00048
    00049   uint sx = TileX(this->tile);
    00050   uint sy = TileY(this->tile);
    00051   uint ex = sx + this->w - 1;
    00052   uint ey = sy + this->h - 1;
    00053
    00054   uint ax = TileX(to_add);
    00055   uint ay = TileY(to_add);
    00056
    00057   sx = min(ax, sx);
    00058   sy = min(ay, sy);
    00059   ex = max(ax, ex);
    00060   ey = max(ay, ey);
    00061
    00062   this->tile = TileXY(sx, sy);
    00063   this->w    = ex - sx + 1;
    00064   this->h    = ey - sy + 1;
    00065 }
    00066
    00072 bool TileArea::Intersects(const TileArea &ta) const
    00073 {
    00074   if (ta.w == 0 || this->w == 0) return false;
    00075
    00076   assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0);
    00077
    00078   uint left1   = TileX(this->tile);
    00079   uint top1    = TileY(this->tile);
    00080   uint right1  = left1 + this->w - 1;
    00081   uint bottom1 = top1  + this->h - 1;
    00082
    00083   uint left2   = TileX(ta.tile);
    00084   uint top2    = TileY(ta.tile);
    00085   uint right2  = left2 + ta.w - 1;
    00086   uint bottom2 = top2  + ta.h - 1;
    00087
    00088   return !(
    00089       left2   > right1  ||
    00090       right2  < left1   ||
    00091       top2    > bottom1 ||
    00092       bottom2 < top1
    00093     );
    00094 }
    00095
    00099 void TileArea::ClampToMap()
    00100 {
    00101   assert(this->tile < MapSize());
    00102   this->w = min(this->w, MapSizeX() - TileX(this->tile));
    00103   this->h = min(this->h, MapSizeY() - TileY(this->tile));
    00104 }
    00105
    00111 DiagonalTileIterator::DiagonalTileIterator(TileIndex corner1, TileIndex corner2) : TileIterator(corner2), base_x(TileX(corner2)), base_y(TileY(corner2)), a_cur(0), b_cur(0)
    00112 {
    00113   assert(corner1 < MapSize());
    00114   assert(corner2 < MapSize());
    00115
    00116   int dist_x = TileX(corner1) - TileX(corner2);
    00117   int dist_y = TileY(corner1) - TileY(corner2);
    00118   this->a_max = dist_x + dist_y;
    00119   this->b_max = dist_y - dist_x;
    00120
    00121   /* Unfortunately we can't find a new base and make all a and b positive because
    00122    * the new base might be a "flattened" corner where there actually is no single
    00123    * tile. If we try anyway the result is either inaccurate ("one off" half of the
    00124    * time) or the code gets much more complex;
    00125    *
    00126    * We also need to increment here to have equality as marker for the end of a row or
    00127    * column. Like that it's shorter than having another if/else in operator++
    00128    */
    00129   if (this->a_max > 0) {
    00130     this->a_max++;
    00131   } else {
    00132     this->a_max--;
    00133   }
    00134
    00135   if (this->b_max > 0) {
    00136     this->b_max++;
    00137   } else {
    00138     this->b_max--;
    00139   }
    00140 }
    00141
    00145 TileIterator &DiagonalTileIterator::operator++()
    00146 {
    00147   assert(this->tile != INVALID_TILE);
    00148
    00149   /* Determine the next tile, while clipping at map borders */
    00150   bool new_line = false;
    00151   do {
    00152     /* Iterate using the rotated coordinates. */
    00153     if (this->a_max == 1 || this->a_max == -1) {
    00154       /* Special case: Every second column has zero length, skip them completely */
    00155       this->a_cur = 0;
    00156       if (this->b_max > 0) {
    00157         this->b_cur = min(this->b_cur + 2, this->b_max);
    00158       } else {
    00159         this->b_cur = max(this->b_cur - 2, this->b_max);
    00160       }
    00161     } else {
    00162       /* Every column has at least one tile to process */
    00163       if (this->a_max > 0) {
    00164         this->a_cur += 2;
    00165         new_line = this->a_cur >= this->a_max;
    00166       } else {
    00167         this->a_cur -= 2;
    00168         new_line = this->a_cur <= this->a_max;
    00169       }
    00170       if (new_line) {
    00171         /* offset of initial a_cur: one tile in the same direction as a_max
    00172          * every second line.
    00173          */
    00174         this->a_cur = abs(this->a_cur) % 2 ? 0 : (this->a_max > 0 ? 1 : -1);
    00175
    00176         if (this->b_max > 0) {
    00177           ++this->b_cur;
    00178         } else {
    00179           --this->b_cur;
    00180         }
    00181       }
    00182     }
    00183
    00184     /* And convert the coordinates back once we've gone to the next tile. */
    00185     uint x = this->base_x + (this->a_cur - this->b_cur) / 2;
    00186     uint y = this->base_y + (this->b_cur + this->a_cur) / 2;
    00187     /* Prevent wrapping around the map's borders. */
    00188     this->tile = x >= MapSizeX() || y >= MapSizeY() ? INVALID_TILE : TileXY(x, y);
    00189   } while (this->tile > MapSize() && this->b_max != this->b_cur);
    00190
    00191   if (this->b_max == this->b_cur) this->tile = INVALID_TILE;
    00192   return *this;
    00193 }