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 }