# tilearea.cpp

Go to the documentation of this file.

    00001 /* $Id: tilearea.cpp 26289 2014-02-02 14:53:26Z fonsinchen $ */
    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 OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end)
    00022 {
    00023   assert(start < MapSize());
    00024   assert(end < MapSize());
    00025
    00026   uint sx = TileX(start);
    00027   uint sy = TileY(start);
    00028   uint ex = TileX(end);
    00029   uint ey = TileY(end);
    00030
    00031   if (sx > ex) Swap(sx, ex);
    00032   if (sy > ey) Swap(sy, ey);
    00033
    00034   this->tile = TileXY(sx, sy);
    00035   this->w    = ex - sx + 1;
    00036   this->h    = ey - sy + 1;
    00037 }
    00038
    00043 void OrthogonalTileArea::Add(TileIndex to_add)
    00044 {
    00045   if (this->tile == INVALID_TILE) {
    00046     this->tile = to_add;
    00047     this->w = 1;
    00048     this->h = 1;
    00049     return;
    00050   }
    00051
    00052   uint sx = TileX(this->tile);
    00053   uint sy = TileY(this->tile);
    00054   uint ex = sx + this->w - 1;
    00055   uint ey = sy + this->h - 1;
    00056
    00057   uint ax = TileX(to_add);
    00058   uint ay = TileY(to_add);
    00059
    00060   sx = min(ax, sx);
    00061   sy = min(ay, sy);
    00062   ex = max(ax, ex);
    00063   ey = max(ay, ey);
    00064
    00065   this->tile = TileXY(sx, sy);
    00066   this->w    = ex - sx + 1;
    00067   this->h    = ey - sy + 1;
    00068 }
    00069
    00075 bool OrthogonalTileArea::Intersects(const OrthogonalTileArea &ta) const
    00076 {
    00077   if (ta.w == 0 || this->w == 0) return false;
    00078
    00079   assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0);
    00080
    00081   uint left1   = TileX(this->tile);
    00082   uint top1    = TileY(this->tile);
    00083   uint right1  = left1 + this->w - 1;
    00084   uint bottom1 = top1  + this->h - 1;
    00085
    00086   uint left2   = TileX(ta.tile);
    00087   uint top2    = TileY(ta.tile);
    00088   uint right2  = left2 + ta.w - 1;
    00089   uint bottom2 = top2  + ta.h - 1;
    00090
    00091   return !(
    00092       left2   > right1  ||
    00093       right2  < left1   ||
    00094       top2    > bottom1 ||
    00095       bottom2 < top1
    00096     );
    00097 }
    00098
    00104 bool OrthogonalTileArea::Contains(TileIndex tile) const
    00105 {
    00106   if (this->w == 0) return false;
    00107
    00108   assert(this->w != 0 && this->h != 0);
    00109
    00110   uint left   = TileX(this->tile);
    00111   uint top    = TileY(this->tile);
    00112   uint tile_x = TileX(tile);
    00113   uint tile_y = TileY(tile);
    00114
    00115   return IsInsideBS(tile_x, left, this->w) && IsInsideBS(tile_y, top, this->h);
    00116 }
    00117
    00121 void OrthogonalTileArea::ClampToMap()
    00122 {
    00123   assert(this->tile < MapSize());
    00124   this->w = min(this->w, MapSizeX() - TileX(this->tile));
    00125   this->h = min(this->h, MapSizeY() - TileY(this->tile));
    00126 }
    00127
    00133 DiagonalTileArea::DiagonalTileArea(TileIndex start, TileIndex end) : tile(start)
    00134 {
    00135   assert(start < MapSize());
    00136   assert(end < MapSize());
    00137
    00138   /* Unfortunately we can't find a new base and make all a and b positive because
    00139    * the new base might be a "flattened" corner where there actually is no single
    00140    * tile. If we try anyway the result is either inaccurate ("one off" half of the
    00141    * time) or the code gets much more complex;
    00142    *
    00143    * We also need to increment/decrement a and b here to have one-past-end semantics
    00144    * for a and b, just the way the orthogonal tile area does it for w and h. */
    00145
    00146   this->a = TileY(end) + TileX(end) - TileY(start) - TileX(start);
    00147   this->b = TileY(end) - TileX(end) - TileY(start) + TileX(start);
    00148   if (this->a > 0) {
    00149     this->a++;
    00150   } else {
    00151     this->a--;
    00152   }
    00153
    00154   if (this->b > 0) {
    00155     this->b++;
    00156   } else {
    00157     this->b--;
    00158   }
    00159 }
    00160
    00166 bool DiagonalTileArea::Contains(TileIndex tile) const
    00167 {
    00168   int a = TileY(tile) + TileX(tile);
    00169   int b = TileY(tile) - TileX(tile);
    00170
    00171   int start_a = TileY(this->tile) + TileX(this->tile);
    00172   int start_b = TileY(this->tile) - TileX(this->tile);
    00173
    00174   int end_a = start_a + this->a;
    00175   int end_b = start_b + this->b;
    00176
    00177   /* Swap if necessary, preserving the "one past end" semantics. */
    00178   if (start_a > end_a) {
    00179     int tmp = start_a;
    00180     start_a = end_a + 1;
    00181     end_a = tmp + 1;
    00182   }
    00183   if (start_b > end_b) {
    00184     int tmp = start_b;
    00185     start_b = end_b + 1;
    00186     end_b = tmp + 1;
    00187   }
    00188
    00189   return (a >= start_a && a < end_a && b >= start_b && b < end_b);
    00190 }
    00191
    00195 TileIterator &DiagonalTileIterator::operator++()
    00196 {
    00197   assert(this->tile != INVALID_TILE);
    00198
    00199   /* Determine the next tile, while clipping at map borders */
    00200   bool new_line = false;
    00201   do {
    00202     /* Iterate using the rotated coordinates. */
    00203     if (this->a_max == 1 || this->a_max == -1) {
    00204       /* Special case: Every second column has zero length, skip them completely */
    00205       this->a_cur = 0;
    00206       if (this->b_max > 0) {
    00207         this->b_cur = min(this->b_cur + 2, this->b_max);
    00208       } else {
    00209         this->b_cur = max(this->b_cur - 2, this->b_max);
    00210       }
    00211     } else {
    00212       /* Every column has at least one tile to process */
    00213       if (this->a_max > 0) {
    00214         this->a_cur += 2;
    00215         new_line = this->a_cur >= this->a_max;
    00216       } else {
    00217         this->a_cur -= 2;
    00218         new_line = this->a_cur <= this->a_max;
    00219       }
    00220       if (new_line) {
    00221         /* offset of initial a_cur: one tile in the same direction as a_max
    00222          * every second line.
    00223          */
    00224         this->a_cur = abs(this->a_cur) % 2 ? 0 : (this->a_max > 0 ? 1 : -1);
    00225
    00226         if (this->b_max > 0) {
    00227           ++this->b_cur;
    00228         } else {
    00229           --this->b_cur;
    00230         }
    00231       }
    00232     }
    00233
    00234     /* And convert the coordinates back once we've gone to the next tile. */
    00235     uint x = this->base_x + (this->a_cur - this->b_cur) / 2;
    00236     uint y = this->base_y + (this->b_cur + this->a_cur) / 2;
    00237     /* Prevent wrapping around the map's borders. */
    00238     this->tile = x >= MapSizeX() || y >= MapSizeY() ? INVALID_TILE : TileXY(x, y);
    00239   } while (this->tile > MapSize() && this->b_max != this->b_cur);
    00240
    00241   if (this->b_max == this->b_cur) this->tile = INVALID_TILE;
    00242   return *this;
    00243 }