Main   Class Hierarchy   Classes   Compound List   Files   Compound Members   File Members   Pages  

tiling.cpp

Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 /** @file tiling.cpp
00003     @see tiling.h
00004 
00005     $Id: tiling_8cpp-source.html,v 1.1.1.1 2003/08/07 19:41:39 emarkus Exp $
00006     $Source: /usr/cvsroot/www_pathfind/libpathfind/0.1.0/doc/tiling_8cpp-source.html,v $
00007 */
00008 //-----------------------------------------------------------------------------
00009 
00010 #include <assert.h>
00011 #include "tiling.h"
00012 
00013 #include <ctype.h>
00014 #include <iostream>
00015 #include <sstream>
00016 #include "search.h"
00017 
00018 using namespace std;
00019 using namespace PathFind;
00020 
00021 #define MAXLINE 2048
00022 
00023 //-----------------------------------------------------------------------------
00024 
00025 TilingNodeInfo::TilingNodeInfo()
00026     : m_isObstacle(false),
00027       m_column(-1),
00028       m_row(-1)
00029 {
00030 }
00031 
00032 TilingNodeInfo::TilingNodeInfo(bool isObstacle, int row, int column)
00033     : m_isObstacle(isObstacle),
00034       m_column(column),
00035       m_row(row)
00036 {
00037 }
00038 
00039 //-----------------------------------------------------------------------------
00040 
00041 Tiling::Tiling(Type type, int rows, int columns)
00042 {
00043     init(type, rows, columns);
00044 }
00045 
00046 Tiling::Tiling(const Tiling & tiling, int horizOrigin, int vertOrigin,
00047                int width, int height)
00048 {
00049     // init builds everything, except for the obstacles...
00050     init(tiling.getType(), width, height);
00051     // so we now put the obstacles in place
00052     for (int col = 0; col < width; col++)
00053     for (int row = 0; row < height; row++)
00054     {
00055         // get the local node
00056         int localNodeId = getNodeId(row, col);
00057         TilingNodeInfo& localNodeInfo = m_graph.getNodeInfo(localNodeId);
00058         // get the initial tiling node
00059         int nodeId = tiling.getNodeId(vertOrigin + row, horizOrigin + col);
00060         const TilingNodeInfo& nodeInfo = tiling.getNodeInfo(nodeId);
00061         // set obstacle for the local node
00062         if (nodeInfo.isObstacle())
00063             localNodeInfo.setObstacle(true);
00064         else
00065             localNodeInfo.setObstacle(false);
00066     }
00067 }
00068 
00069 Tiling::Tiling(LineReader& reader)
00070 {
00071     int columns = -1;
00072     int rows = -1;
00073     bool typeFound = false;
00074     Type type = TILE;
00075     bool done = false;
00076     while (! done)
00077     {
00078         string line = reader.readLine();
00079         if (line == "")
00080             continue;
00081         if (line.size() > 0 && line[0] == '#')
00082             continue;
00083         istringstream in(line);
00084         string attribute;
00085         in >> attribute;
00086         if (! in)
00087             throw reader.createError("Missing attribute.");
00088         if (attribute == "type")
00089         {
00090             string typeString;
00091             in >> typeString;
00092             if (! in)
00093                 throw reader.createError("Missing type value.");
00094             if (typeString == "tile")
00095                 type = TILE;
00096             else if (typeString == "octile")
00097                 type = OCTILE;
00098             else if (typeString == "octile_unicost")
00099                 type = OCTILE_UNICOST;
00100             else if (typeString == "hex")
00101                 type = HEX;
00102             else
00103                 throw reader.createError("Invalid type value.");
00104             typeFound = true;
00105         }
00106         else if (attribute == "width")
00107         {
00108             in >> columns;
00109             if (! in || columns <= 0 || columns > LineReader::MAX_LINE)
00110                 throw reader.createError("Invalid width.");
00111         }
00112         else if (attribute == "height")
00113         {
00114             in >> rows;
00115             if (! in || rows <= 0)
00116                 throw reader.createError("Invalid height.");
00117         }
00118         else if (attribute == "map")
00119         {
00120             done = true;
00121         }
00122         else
00123             throw reader.createError("Unknown attribute.");
00124     }
00125     if (columns == -1 || rows == -1)
00126         throw reader.createError("Map without valid width / height.");
00127     if (! typeFound)
00128         throw reader.createError("Map without type.");
00129     init(type, rows, columns);
00130     readObstacles(reader);
00131 }
00132 
00133 void Tiling::addOutEdge(int nodeId, int row, int col, int cost)
00134 {
00135     if (row < 0 || row >= m_rows || col < 0 || col >= m_columns)
00136         return;
00137     m_graph.addOutEdge(nodeId, getNodeId(row, col), TilingEdgeInfo(cost));
00138 }
00139 
00140 void Tiling::clearObstacles()
00141 {
00142     int numberNodes = getNumberNodes();
00143     for (int nodeId = 0; nodeId < numberNodes; ++nodeId)
00144     {
00145         TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId);
00146         nodeInfo.setObstacle(false);
00147     }
00148 }
00149 
00150 void Tiling::createEdges()
00151 {
00152     assert(m_type == TILE
00153            || m_type == OCTILE
00154            || m_type == OCTILE_UNICOST
00155            || m_type == HEX);
00156     for (int row = 0; row < m_rows; ++row)
00157         for (int col = 0; col < m_columns; ++col)
00158         {
00159             int nodeId = getNodeId(row, col);
00160             addOutEdge(nodeId, row - 1, col, COST_ONE);
00161             addOutEdge(nodeId, row + 1, col, COST_ONE);
00162             addOutEdge(nodeId, row, col - 1, COST_ONE);
00163             addOutEdge(nodeId, row, col + 1, COST_ONE);
00164             if (m_type == OCTILE)
00165             {
00166                 addOutEdge(nodeId, row + 1, col + 1, COST_SQRT2);
00167                 addOutEdge(nodeId, row + 1, col - 1, COST_SQRT2);
00168                 addOutEdge(nodeId, row - 1, col + 1, COST_SQRT2);
00169                 addOutEdge(nodeId, row - 1, col - 1, COST_SQRT2);
00170             }
00171             else if (m_type == OCTILE_UNICOST)
00172             {
00173                 addOutEdge(nodeId, row + 1, col + 1, COST_ONE);
00174                 addOutEdge(nodeId, row + 1, col - 1, COST_ONE);
00175                 addOutEdge(nodeId, row - 1, col + 1, COST_ONE);
00176                 addOutEdge(nodeId, row - 1, col - 1, COST_ONE);
00177             }
00178             else if (m_type == HEX)
00179             {
00180                 if (col % 2 == 0)
00181                 {
00182                     addOutEdge(nodeId, row - 1, col + 1, COST_ONE);
00183                     addOutEdge(nodeId, row - 1, col - 1, COST_ONE);
00184                 }
00185                 else
00186                 {
00187                     addOutEdge(nodeId, row + 1, col + 1, COST_ONE);
00188                     addOutEdge(nodeId, row + 1, col - 1, COST_ONE);
00189                 }
00190             }
00191         }
00192 }
00193 
00194 void Tiling::createNodes()
00195 {
00196     for (int row = 0; row < m_rows; ++row)
00197         for (int col = 0; col < m_columns; ++col)
00198         {
00199             int nodeId = getNodeId(row, col);
00200             m_graph.addNode(nodeId, TilingNodeInfo(false, row, col));
00201         }
00202 }
00203 
00204 vector<char> Tiling::getCharVector() const
00205 {
00206     vector<char> result;
00207     int numberNodes = getNumberNodes();
00208     result.reserve(numberNodes);
00209     for (int i = 0; i < numberNodes; ++i)
00210     {
00211         if (m_graph.getNodeInfo(i).isObstacle())
00212             result.push_back('@');
00213         else
00214             result.push_back('.');
00215     }
00216     return result;
00217 }
00218 
00219 int Tiling::getHeuristic(int start, int target) const
00220 {
00221     int colStart = start % m_columns;
00222     int colTarget = target % m_columns;
00223     int rowStart = start / m_columns;
00224     int rowTarget = target / m_columns;
00225     int diffCol = abs(colTarget - colStart);
00226     int diffRow = abs(rowTarget - rowStart);
00227     switch (m_type)
00228     {
00229     case HEX:
00230         // Vancouver distance
00231         // See P.Yap: Grid-based Path-Finding (LNAI 2338 pp.44-55)
00232         {
00233             int correction = 0;
00234             if (diffCol % 2 != 0)
00235             {
00236                 if (rowTarget < rowStart)
00237                     correction = colTarget % 2;
00238                 else if (rowTarget > rowStart)
00239                     correction = colStart % 2;
00240             }
00241             // Note: formula in paper is wrong, corrected below.  
00242             int dist = max(0, diffRow - diffCol / 2 - correction) + diffCol;
00243             return dist * COST_ONE;
00244         }
00245     case OCTILE_UNICOST:
00246         return max(diffCol, diffRow) * COST_ONE;
00247     case OCTILE:
00248         int maxDiff;
00249         int minDiff;
00250         if (diffCol > diffRow)
00251         {
00252             maxDiff = diffCol;
00253             minDiff = diffRow;
00254         }
00255         else
00256         {
00257             maxDiff = diffRow;
00258             minDiff = diffCol;
00259         }
00260         return minDiff * COST_SQRT2 + (maxDiff - minDiff) * COST_ONE;
00261     case TILE:
00262         return (diffCol + diffRow) * COST_ONE;
00263     default:
00264         assert(false);
00265         return 0;
00266     }
00267 }
00268         
00269 int Tiling::getMaxCost() const
00270 {
00271     if (m_type == OCTILE)
00272         return COST_SQRT2;
00273     return COST_ONE;
00274 }
00275 
00276 int Tiling::getMaxEdges(Type type)
00277 {
00278     switch (type)
00279     {
00280     case HEX:
00281         return 6;
00282     case OCTILE:
00283     case OCTILE_UNICOST:
00284         return 8;
00285     case TILE:
00286         return 4;
00287     }
00288     assert(false);
00289     return 0;
00290 }
00291 
00292 int Tiling::getMinCost() const
00293 {
00294     return COST_ONE;
00295 }
00296 
00297 int Tiling::getNumberNodes() const
00298 {
00299     return m_rows * m_columns;
00300 }
00301 
00302 void Tiling::getSuccessors(int nodeId, int lastNodeId,
00303                            vector<Successor>& result) const
00304 {
00305     result.reserve(m_maxEdges);
00306     result.clear();
00307     const TilingNode& node = m_graph.getNode(nodeId);
00308     const TilingNodeInfo& nodeInfo = node.getInfo();
00309     if (nodeInfo.isObstacle())
00310     {
00311         assert(result.size() == 0);
00312         return;
00313     }
00314     const vector<TilingEdge>& edges = node.getOutEdges(); 
00315     for (vector<TilingEdge>::const_iterator i = edges.begin();
00316          i != edges.end(); ++i)
00317     {
00318         int targetNodeId = i->getTargetNodeId();
00319         assert(isValidNodeId(targetNodeId));
00320         const TilingNode& targetNode = m_graph.getNode(targetNodeId);
00321         const TilingNodeInfo& targetNodeInfo = targetNode.getInfo();
00322         if (targetNodeInfo.isObstacle())
00323             continue;
00324         if (lastNodeId != NO_NODE)
00325             if (pruneNode(targetNodeId, lastNodeId))
00326                 continue;
00327         result.push_back(Successor(targetNodeId, i->getInfo().getCost()));
00328     }
00329 #ifndef NDEBUG
00330     int resultSize = result.size();
00331     assert(resultSize <= m_maxEdges);
00332     if (lastNodeId != NO_NODE)
00333         switch (m_type)
00334         {
00335         case HEX:
00336         case TILE:
00337             assert(resultSize <= 3);
00338             break;
00339         case OCTILE:
00340         case OCTILE_UNICOST:
00341             assert(resultSize <= 5);
00342             break;
00343         }
00344 #endif
00345 }
00346 
00347 void Tiling::init(Type type, int rows, int columns)
00348 {
00349     m_type = type;
00350     m_maxEdges = getMaxEdges(type);
00351     m_rows = rows;
00352     m_columns = columns;
00353     m_graph.clear();
00354     createNodes();
00355     createEdges();
00356 }
00357 
00358 bool Tiling::isValidNodeId(int nodeId) const
00359 {
00360     return nodeId >= 0 && nodeId < getNumberNodes();
00361 }
00362 
00363 void Tiling::printFormatted(ostream& o) const
00364 {
00365     printFormatted(o, getCharVector());
00366 }
00367 
00368 void Tiling::printFormatted(ostream& o, const vector<char>& chars) const
00369 {
00370     for (int row = 0; row < m_rows; ++row)
00371     {
00372         for (int col = 0; col < m_columns; ++col)
00373         {
00374             int nodeId = getNodeId(row, col);
00375             o << chars[nodeId];
00376         }
00377         o << '\n';
00378     }
00379 }
00380 
00381 void Tiling::printFormatted(ostream& o, int start, int target) const
00382 {
00383     vector<char> chars = getCharVector();
00384     chars[start] = 'S';
00385     chars[target] = 'T';
00386     printFormatted(o, chars);
00387 }
00388 
00389 void Tiling::printFormatted(ostream& o, const vector<int>& path) const
00390 {
00391     vector<char> chars = getCharVector();
00392     if (path.size() > 0)
00393     {
00394         for (vector<int>::const_iterator i = path.begin();
00395              i != path.end(); ++i)
00396             chars[*i] = 'x';
00397         chars[*path.begin()] = 'T';
00398         chars[*(path.end() - 1)] = 'S';
00399     }
00400     printFormatted(o, chars);
00401 }
00402 
00403 void Tiling::printLabels(ostream& o, const vector<char>& labels) const
00404 {
00405     assert(labels.size() == static_cast<unsigned int>(getNumberNodes()));
00406     vector<char> chars = getCharVector();
00407     vector<char>::iterator j = chars.begin();
00408     for (vector<char>::const_iterator i = labels.begin();
00409          i != labels.end(); ++i, ++j)
00410         if (*i != ' ')
00411             *j = *i;
00412     printFormatted(o, chars);
00413 }
00414 
00415 
00416 void Tiling::printPathAndLabels(ostream& o, const vector<int>& path,
00417                                 const vector<char>& labels) const
00418 {
00419     assert(labels.size() == static_cast<unsigned int>(getNumberNodes()));
00420 
00421     vector<char> chars = getCharVector();
00422 
00423     vector<char>::iterator j = chars.begin();
00424     for (vector<char>::const_iterator i = labels.begin();
00425          i != labels.end(); ++i, ++j)
00426         if (*i != ' ')
00427             *j = *i;
00428 
00429     if (path.size() > 0)
00430     {
00431         for (vector<int>::const_iterator i = path.begin();
00432              i != path.end(); ++i)
00433             chars[*i] = 'x';
00434         chars[*path.begin()] = 'T';
00435         chars[*(path.end() - 1)] = 'S';
00436     }
00437 
00438     printFormatted(o, chars);
00439 }
00440 
00441 
00442 bool Tiling::pruneNode(int targetNodeId, int lastNodeId) const
00443 {
00444     if (targetNodeId == lastNodeId)
00445         return true;
00446     if (m_type == TILE)
00447         return false;
00448     const TilingNode& lastNode = m_graph.getNode(lastNodeId);
00449     const vector<TilingEdge>& edges = lastNode.getOutEdges(); 
00450     for (vector<TilingEdge>::const_iterator i = edges.begin();
00451          i != edges.end(); ++i)
00452     {
00453         if (i->getTargetNodeId() == targetNodeId)
00454             return true;
00455     }
00456     return false;
00457 }
00458 
00459 void Tiling::readObstacles(LineReader& reader)
00460 {
00461     for (int row = 0; row < m_rows; ++row)
00462     {
00463         string line = reader.readLine();
00464         istringstream in(line);
00465         for (int col = 0; col < m_columns; ++col)
00466         {
00467             bool isObstacle;
00468             char c;
00469             in.get(c);
00470             if (! in)
00471                 throw reader.createError("Unexpected end of stream.");
00472             if (c == '@')
00473                 isObstacle = true;
00474             else if (c == '.')
00475                 isObstacle = false;
00476             else
00477                 throw reader.createError("Unknown charcter.");
00478             int nodeId = getNodeId(row, col);
00479             TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId);
00480             if (! nodeInfo.isObstacle())
00481                 nodeInfo.setObstacle(isObstacle);
00482             
00483         }
00484     }
00485 }
00486 
00487 
00488 bool Tiling::conflictDiag(int row, int col, int roff, int coff)
00489 {
00490     // Avoid generating cofigurations like:
00491     //
00492     //    @   or   @
00493     //     @      @
00494     //
00495     // that favor one grid topology over another.
00496     if ( (row+roff < 0) || (row+roff >= m_rows) || 
00497          (col+coff < 0) || (col+coff >= m_columns) )
00498         return false;
00499 
00500     if ( (m_graph.getNodeInfo(getNodeId(row+roff,col+coff))).isObstacle() )
00501     {
00502         if ( !m_graph.getNodeInfo(getNodeId(row,col+coff)).isObstacle() &&
00503              !m_graph.getNodeInfo(getNodeId(row+roff,col)).isObstacle() )
00504         return true;
00505     }
00506     return false;
00507 } 
00508 
00509 
00510 void Tiling::setObstacles(float obstaclePercentage, bool avoidDiag )
00511 {
00512     clearObstacles();
00513     int numberNodes = getNumberNodes();
00514     int numberObstacles = static_cast<int>(obstaclePercentage * numberNodes);
00515     for (int count = 0; count < numberObstacles; )
00516     {
00517         int nodeId = rand() / (RAND_MAX / numberNodes + 1);
00518         TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId);
00519         if (! nodeInfo.isObstacle())
00520         {
00521             if ( avoidDiag )
00522             {
00523                 int row = nodeInfo.getRow();
00524                 int col = nodeInfo.getColumn();
00525 
00526                 if ( !conflictDiag(row,col,-1,-1) && 
00527                      !conflictDiag(row,col,-1,+1) &&
00528                      !conflictDiag(row,col,+1,-1) &&
00529                      !conflictDiag(row,col,+1,+1) )
00530                 {
00531                     nodeInfo.setObstacle(true);
00532                     ++count;
00533                 }
00534             }
00535             else 
00536             {
00537                 nodeInfo.setObstacle(true);
00538                 ++count;
00539             }
00540         }
00541     }
00542 }
00543 
00544 //-----------------------------------------------------------------------------


Generated on Thu Aug 7 13:04:50 2003 by Doxygen1.3.1