hexgrid.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #include <cassert>
00024 
00025 // 3rd party library includes
00026 
00027 // FIFE includes
00028 // These includes are split up in two parts, separated by one empty line
00029 // First block: files included from the FIFE root src directory
00030 // Second block: files included from the same folder
00031 #include "util/math/fife_math.h"
00032 #include "util/log/logger.h"
00033 
00034 #include "hexgrid.h"
00035 
00036 namespace FIFE {
00037     static Logger _log(LM_HEXGRID);
00038 
00039     static const double HEX_WIDTH = 1;
00040     static const double HEX_TO_EDGE = HEX_WIDTH / 2;
00041     static const double HEX_TO_CORNER = 0.5 / Mathd::Cos(Mathd::pi() / 6);
00042     static const double HEX_EDGE_HALF = HEX_TO_CORNER * Mathd::Sin(Mathd::pi() / 6);
00043     static const double VERTICAL_MULTIP = Mathd::Sqrt(HEX_WIDTH*HEX_WIDTH - HEX_TO_EDGE*HEX_TO_EDGE);
00044     static const double VERTICAL_MULTIP_INV = 1 / VERTICAL_MULTIP;
00045 
00046     HexGrid::HexGrid(bool allow_diagonals): CellGrid(allow_diagonals) {
00047         FL_DBG(_log, "Constructing new HexGrid");
00048         FL_DBG(_log, LMsg("HEX_WIDTH ") << HEX_WIDTH);
00049         FL_DBG(_log, LMsg("HEX_TO_EDGE ") << HEX_TO_EDGE);
00050         FL_DBG(_log, LMsg("HEX_TO_CORNER ") << HEX_TO_CORNER);
00051         FL_DBG(_log, LMsg("HEX_EDGE_HALF ") << HEX_EDGE_HALF);
00052         FL_DBG(_log, LMsg("VERTICAL_MULTIP ") << VERTICAL_MULTIP);
00053     }
00054 
00055     CellGrid* HexGrid::clone() {
00056         HexGrid* nGrid = new HexGrid(m_allow_diagonals);
00057         nGrid->setRotation(m_rotation);
00058         nGrid->setXScale(m_xscale);
00059         nGrid->setYScale(m_yscale);
00060         nGrid->setXShift(m_xshift);
00061         nGrid->setYShift(m_yshift);
00062         nGrid->setZShift(m_zshift);
00063 
00064         return nGrid;
00065     }
00066 
00067     HexGrid::~HexGrid() {
00068     }
00069 
00070     bool HexGrid::isAccessible(const ModelCoordinate& curpos, const ModelCoordinate& target) {
00071         if (curpos == target) {
00072             return true;
00073         }
00074 
00075         if(curpos.y % 2) {
00076 
00077             if((curpos.x == target.x) && (curpos.y - 1 == target.y)) {
00078                 return true;
00079             }
00080 
00081             if((curpos.x + 1 == target.x) && (curpos.y - 1 == target.y)) {
00082                 return true;
00083             }
00084 
00085             if((curpos.x + 1 == target.x) && (curpos.y == target.y)) {
00086                 return true;
00087             }
00088 
00089             if((curpos.x + 1 == target.x) && (curpos.y + 1 == target.y)) {
00090                 return true;
00091             }
00092 
00093             if((curpos.x == target.x) && (curpos.y + 1 == target.y)) {
00094                 return true;
00095             }
00096 
00097             if((curpos.x - 1 == target.x) && (curpos.y == target.y)) {
00098                 return true;
00099             }
00100 
00101         } else {
00102 
00103             if((curpos.x - 1 == target.x) && (curpos.y - 1 == target.y)) {
00104                 return true;
00105             }
00106 
00107             if((curpos.x == target.x) && (curpos.y - 1 == target.y)) {
00108                 return true;
00109             }
00110 
00111             if((curpos.x + 1 == target.x) && (curpos.y == target.y)) {
00112                 return true;
00113             }
00114 
00115             if((curpos.x  == target.x) && (curpos.y + 1 == target.y)) {
00116                 return true;
00117             }
00118 
00119             if((curpos.x - 1 == target.x) && (curpos.y + 1 == target.y)) {
00120                 return true;
00121             }
00122 
00123             if((curpos.x - 1 == target.x) && (curpos.y == target.y)) {
00124                 return true;
00125             }
00126         }
00127 
00128         return false;
00129 
00130     }
00131 
00132     double HexGrid::getAdjacentCost(const ModelCoordinate& curpos, const ModelCoordinate& target) {
00133         assert(isAccessible(curpos, target));
00134         if (curpos == target) {
00135             return 0;
00136         } else if (curpos.y == target.y) {
00137             return m_xscale;
00138         } else {
00139             double a = VERTICAL_MULTIP * m_yscale;
00140             double b = HEX_TO_EDGE * m_xscale;
00141             return Mathd::Sqrt((a * a) + (b * b));
00142         }
00143     }
00144 
00145     const std::string& HexGrid::getType() const {
00146         static std::string type("hexagonal");
00147         return type;
00148     }
00149 
00150     const std::string& HexGrid::getName() const {
00151         static std::string hexGrid("Hex Grid");
00152         return hexGrid;
00153     }
00154 
00155     double HexGrid::getXZigzagOffset(double y) {
00156         // each uneven row has shifted coordinate of 0.5 horizontally
00157         // shift has to be gradual on vertical axis
00158         double ay = ABS(y);
00159         int32_t i_layer_y = static_cast<int32_t>(ay);
00160         double offset = ay - static_cast<double>(i_layer_y);
00161         if ((i_layer_y % 2) == 1) {
00162             offset = 1 - offset;
00163         }
00164         return HEX_TO_EDGE * offset;
00165     }
00166 
00167     ExactModelCoordinate HexGrid::toMapCoordinates(const ExactModelCoordinate& layer_coords) {
00168         ExactModelCoordinate tranformed_coords(layer_coords);
00169         tranformed_coords.x += getXZigzagOffset(layer_coords.y);
00170         tranformed_coords.y *= VERTICAL_MULTIP;
00171         ExactModelCoordinate result = m_matrix * tranformed_coords;
00172         FL_DBG(_log, LMsg("layercoords ") << layer_coords << " converted to map: " << result);
00173         return result;
00174     }
00175 
00176     ExactModelCoordinate HexGrid::toExactLayerCoordinates(const ExactModelCoordinate& map_coord) {
00177         ExactModelCoordinate layer_coords = m_inverse_matrix * map_coord;
00178         layer_coords.y /= VERTICAL_MULTIP;
00179         layer_coords.x -= getXZigzagOffset(layer_coords.y);
00180         FL_DBG(_log, LMsg("mapcoords ") << map_coord << " converted to layer: " << layer_coords);
00181         return layer_coords;
00182     }
00183 
00184     ModelCoordinate HexGrid::toLayerCoordinates(const ExactModelCoordinate& map_coord) {
00185         FL_DBG(_log, LMsg("==============\nConverting map coords ") << map_coord << " to int32_t layer coords...");
00186         ExactModelCoordinate elc = m_inverse_matrix * map_coord;
00187         elc.y *= VERTICAL_MULTIP_INV;
00188         ExactModelCoordinate lc = ExactModelCoordinate(floor(elc.x), floor(elc.y), floor(elc.z));
00189         double dx = elc.x - lc.x;
00190         double dy = elc.y - lc.y;
00191         double dz = elc.z - lc.z;
00192         int32_t x = static_cast<int32_t>(lc.x);
00193         int32_t y = static_cast<int32_t>(lc.y);
00194         int32_t z = static_cast<int32_t>(lc.z);
00195 //      FL_DBG(_log, LMsg("elc=") << elc << ", lc=" << lc);
00196 //      FL_DBG(_log, LMsg("x=") << x << ", y=" << y << ", dx=" << dx << ", dy=" << dy);
00197         ModelCoordinate result;
00198 
00199         if ((y % 2) == 0) {
00200 //          FL_DBG(_log, "In even row");
00201             if ((1 - dy) < HEX_EDGE_HALF) {
00202                 FL_DBG(_log, "In lower rect area");
00203                 result = ModelCoordinate(x, y+1, z);
00204             }
00205             else if (dy < HEX_EDGE_HALF) {
00206 //              FL_DBG(_log, "In upper rect area");
00207                 if (dx > 0.5) {
00208 //                  FL_DBG(_log, "...on right");
00209                     result = ModelCoordinate(x+1, y, z);
00210                 }
00211                 else {
00212 //                  FL_DBG(_log, "...on left");
00213                     result = ModelCoordinate(x, y, z);
00214                 }
00215             }
00216             // in middle triangle area
00217             else {
00218 //              FL_DBG(_log, "In middle triangle area");
00219                 if (dx < 0.5) {
00220 //                  FL_DBG(_log, "In left triangles");
00221                     if (ptInTriangle(ExactModelCoordinate(dx, dy),
00222                                      ExactModelCoordinate(0, VERTICAL_MULTIP * HEX_EDGE_HALF),
00223                                      ExactModelCoordinate(0, VERTICAL_MULTIP * (1-HEX_EDGE_HALF)),
00224                                      ExactModelCoordinate(0.5, VERTICAL_MULTIP * HEX_EDGE_HALF)
00225                                      )) {
00226 //                      FL_DBG(_log, "..upper part");
00227                         result = ModelCoordinate(x, y, z);
00228                     } else {
00229 //                      FL_DBG(_log, "..lower part");
00230                         result = ModelCoordinate(x, y+1, z);
00231                     }
00232                 } else {
00233 //                  FL_DBG(_log, "In right triangles");
00234                     if (ptInTriangle(ExactModelCoordinate(dx, dy),
00235                                      ExactModelCoordinate(1, VERTICAL_MULTIP * HEX_EDGE_HALF),
00236                                      ExactModelCoordinate(1, VERTICAL_MULTIP * (1-HEX_EDGE_HALF)),
00237                                      ExactModelCoordinate(0.5, VERTICAL_MULTIP * HEX_EDGE_HALF)
00238                                      )) {
00239 //                      FL_DBG(_log, "..upper part");
00240                         result = ModelCoordinate(x+1, y, z);
00241                     } else {
00242 //                      FL_DBG(_log, "..lower part");
00243                         result = ModelCoordinate(x, y+1, z);
00244                     }
00245                 }
00246             }
00247         }
00248         else {
00249 //          FL_DBG(_log, "In uneven row");
00250             if (dy < HEX_EDGE_HALF) {
00251 //              FL_DBG(_log, "In upper rect area");
00252                 result = ModelCoordinate(x, y, z);
00253             }
00254             else if ((1 - dy) < HEX_EDGE_HALF) {
00255 //              FL_DBG(_log, "In lower rect area");
00256                 if (dx > 0.5) {
00257 //                  FL_DBG(_log, "...on right");
00258                     result = ModelCoordinate(x+1, y+1, z);
00259                 }
00260                 else {
00261 //                  FL_DBG(_log, "...on left");
00262                     result = ModelCoordinate(x, y+1, z);
00263                 }
00264             }
00265             else {
00266 //              FL_DBG(_log, "In middle triangle area");
00267                 if (dx < 0.5) {
00268 //                  FL_DBG(_log, "In left triangles");
00269                     if (ptInTriangle(ExactModelCoordinate(dx, dy),
00270                                      ExactModelCoordinate(0, VERTICAL_MULTIP * HEX_EDGE_HALF),
00271                                      ExactModelCoordinate(0, VERTICAL_MULTIP * (1-HEX_EDGE_HALF)),
00272                                      ExactModelCoordinate(0.5, VERTICAL_MULTIP * (1-HEX_EDGE_HALF))
00273                                      )) {
00274 //                      FL_DBG(_log, "..lower part");
00275                         result = ModelCoordinate(x, y+1, z);
00276                     } else {
00277 //                      FL_DBG(_log, "..upper part");
00278                         result = ModelCoordinate(x, y, z);
00279                     }
00280                 } else {
00281 //                  FL_DBG(_log, "In right triangles");
00282                     if (ptInTriangle(ExactModelCoordinate(dx, dy),
00283                                      ExactModelCoordinate(1, VERTICAL_MULTIP * HEX_EDGE_HALF),
00284                                      ExactModelCoordinate(1, VERTICAL_MULTIP * (1-HEX_EDGE_HALF)),
00285                                      ExactModelCoordinate(0.5, VERTICAL_MULTIP * (1-HEX_EDGE_HALF))
00286                                      )) {
00287 //                          FL_DBG(_log, "..lower part");
00288                         result = ModelCoordinate(x+1, y+1, z);
00289                     } else {
00290 //                      FL_DBG(_log, "..upper part");
00291                         result = ModelCoordinate(x, y, z);
00292                     }
00293                 }
00294             }
00295         }
00296 //      FL_DBG(_log, LMsg("  result = ") << result);
00297         return result;
00298     }
00299 
00300     void HexGrid::getVertices(std::vector<ExactModelCoordinate>& vtx, const ModelCoordinate& cell) {
00301         FL_DBG(_log, LMsg("===============\ngetting vertices for ") << cell);
00302         vtx.clear();
00303         double x = static_cast<double>(cell.x);
00304         double y = static_cast<double>(cell.y);
00305         double horiz_shift = 0;
00306         if (cell.y % 2 != 0) {
00307             horiz_shift = HEX_TO_EDGE;
00308             FL_DBG(_log, "on uneven row");
00309         }
00310         double tx, ty;
00311 
00312         #define ADD_PT(_x, _y) vtx.push_back(ExactModelCoordinate(_x, _y));
00313         // FL_DBG(_log, LMsg("Added point ") << _x << ", " << _y)
00314         ty = y - VERTICAL_MULTIP_INV * HEX_EDGE_HALF;
00315         tx = x - HEX_TO_EDGE - getXZigzagOffset(ty) + horiz_shift;
00316         ADD_PT(tx, ty);
00317 
00318         ty = y - VERTICAL_MULTIP_INV * HEX_TO_CORNER;
00319         tx = x - getXZigzagOffset(ty) + horiz_shift;
00320         ADD_PT(tx, ty);
00321 
00322         ty = y - VERTICAL_MULTIP_INV * HEX_EDGE_HALF;
00323         tx = x + HEX_TO_EDGE - getXZigzagOffset(ty) + horiz_shift;
00324         ADD_PT(tx, ty);
00325 
00326         ty = y + VERTICAL_MULTIP_INV * HEX_EDGE_HALF;
00327         tx = x + HEX_TO_EDGE - getXZigzagOffset(ty) + horiz_shift;
00328         ADD_PT(tx, ty);
00329 
00330         ty = y + VERTICAL_MULTIP_INV * HEX_TO_CORNER;
00331         tx = x - getXZigzagOffset(ty) + horiz_shift;
00332         ADD_PT(tx, ty);
00333 
00334         ty = y + VERTICAL_MULTIP_INV * HEX_EDGE_HALF;
00335         tx = x - HEX_TO_EDGE - getXZigzagOffset(ty) + horiz_shift;
00336         ADD_PT(tx, ty);
00337     }
00338 }