grid.cc

Go to the documentation of this file.
00001 /*                            Package   : libhex
00002  * grid.cc                    Created   : 2007/10/11
00003  *                            Author    : Alex Tingle
00004  *
00005  *    Copyright (C) 2007-2008, Alex Tingle.
00006  *
00007  *    This file is part of the libhex application.
00008  *
00009  *    libhex is free software; you can redistribute it and/or
00010  *    modify it under the terms of the GNU Lesser General Public
00011  *    License as published by the Free Software Foundation; either
00012  *    version 2.1 of the License, or (at your option) any later version.
00013  *
00014  *    libhex is distributed in the hope that it will be useful,
00015  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  *    Lesser General Public License for more details.
00018  *
00019  *    You should have received a copy of the GNU Lesser General Public
00020  *    License along with this library; if not, write to the Free Software
00021  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  */
00023 
00024 #include "hex.h"
00025 
00026 #include <sstream>
00027 #include <cmath>
00028 #include <cerrno>
00029 #include <cassert>
00030 
00031 namespace hex {
00032 
00033 
00034 //
00035 // Point
00036 
00037 
00038 Point::Point(const std::string s) throw (out_of_range,invalid_argument)
00039 {
00040   // Parse strings like: / *[-+]?\d+_ *[-+]?\d+([^\d].*)?/
00041   // E.g.  '1_2' ' +2_ 4' '2_4 # comment'
00042   const char* buf =s.c_str();
00043   char* endptr =NULL;
00044   errno=0;
00045   this->x = ::strtod(buf,&endptr);
00046   if(endptr==buf || *endptr!=',')
00047       throw hex::invalid_argument(s);
00048   buf =endptr+1;
00049   this->y = ::strtod(buf,&endptr);
00050   if(endptr==buf)
00051       throw hex::invalid_argument(s);
00052   if(ERANGE==errno)
00053       throw hex::out_of_range(s);
00054 }
00055 
00056 
00057 std::string
00058 Point::str(void) const
00059 {
00060   std::ostringstream ss;
00061   ss<< this->x << "," << this->y;
00062   return ss.str();
00063 }
00064 
00065 
00066 //
00067 // Grid
00068 
00069 
00070 Hex*
00071 Grid::hex(int i, int j) const throw(hex::out_of_range)
00072 {
00073   int key =Hex::_key(i,j);
00074   std::map<int,Hex*>::const_iterator pos =_hexes.find(key);
00075   if(pos==_hexes.end())
00076   {
00077     if(0>i || i>=_cols)
00078         throw hex::out_of_range("i");
00079     if(0>j || j>=_rows)
00080         throw hex::out_of_range("j");
00081     Hex* newhex = new Hex(*this,i,j);
00082     _hexes.insert( std::make_pair(key,newhex) );
00083     return newhex;
00084   }
00085   return pos->second;
00086 }
00087 
00088 
00089 Hex*
00090 Grid::hex(Distance x, Distance y) const throw(hex::out_of_range)
00091 {
00092   // (Note I==1.0, so the factor of I has been omitted.)
00093   const static Distance K_2 =K/2.0;
00094   // BI is unit vector in direction B
00095   const static Distance BIx = 0.5;
00096   const static Distance BIy = 1.5 * K;
00097   // CI is unit vector in direction C
00098   const static Distance CIx = -BIx;
00099   const static Distance CIy =  BIy;
00100 
00101   // Calculate the 'simple' solution.
00102   y -= K;
00103   int j = lround( y/J );
00104   if(j % 2)
00105       x -= 1.0; // odd rows
00106   else
00107       x -= 0.5; // even rows
00108   int i = lround( x );                                              //   x / I
00109   // Now calculate the x,y offsets (in units of (I,J) )
00110   Distance dx = x - Distance(i);                                    //   i * I
00111   Distance dy = y - Distance(j) * J;
00112   // Only need more work if |dy| > K/2
00113   if( dy < -K_2 || K_2 < dy )
00114   {
00115     Distance BId = (BIx * dx) + (BIy * dy);
00116     Distance CId = (CIx * dx) + (CIy * dy);
00117 
00118     if(      BId >  0.5 )
00119                           go( i,j, B );
00120     else if( BId < -0.5 )
00121                           go( i,j, E );
00122     else if( CId >  0.5 )
00123                           go( i,j, C );
00124     else if( CId < -0.5 )
00125                           go( i,j, F );
00126   }
00127   return hex(i,j);
00128 }
00129 
00130 
00131 Hex*
00132 Grid::hex(const Point& p) const throw(hex::out_of_range)
00133 {
00134   return hex(p.x,p.y);
00135 }
00136 
00137 
00138 Area
00139 Grid::to_area(void) const
00140 {
00141   using namespace std;
00142   set<Hex*> hexes;
00143   for(int i=0; i<_cols; ++i)
00144       for(int j=0; j<_rows; ++j)
00145           hexes.insert( hex(i,j) );
00146   return Area(hexes);  
00147 }
00148 
00149 
00150 Hex*
00151 Grid::hex(const std::string& s) const throw(out_of_range,invalid_argument)
00152 {
00153   // Parse strings like: / *[-+]?\d+_ *[-+]?\d+([^\d].*)?/
00154   // E.g.  '1_2' ' +2_ 4' '2_4 # comment'
00155   const char* buf =s.c_str();
00156   char* endptr =NULL;
00157   errno=0;
00158   int i =::strtol(buf,&endptr,10);
00159   if(endptr==buf || *endptr!='_')
00160       throw hex::invalid_argument(s);
00161   buf =endptr+1;
00162   int j =::strtol(buf,&endptr,10);
00163   if(endptr==buf)
00164       throw hex::invalid_argument(s);
00165   if(ERANGE==errno)
00166       throw hex::out_of_range(s);
00167   return this->hex(i,j);
00168 }
00169 
00170 
00171 std::set<Hex*>
00172 Grid::hexes(const std::string& s) const throw(out_of_range,invalid_argument)
00173 {
00174   // Parse string of space-separated hex coordinates.
00175   // E.g. 1_2 2_3 3_4
00176   std::istringstream is(s);
00177   std::set<Hex*> result;
00178   while(is.good())
00179   {
00180     std::string tok;
00181     is>>tok;
00182     if(!tok.empty())
00183         result.insert( this->hex(tok) );
00184   }
00185   return result;
00186 }
00187 
00188 
00189 Area
00190 Grid::area(const std::string& s) const throw(out_of_range,invalid_argument)
00191 {
00192   // Parse string of area fillpaths
00193   // E.g. 1,2>CDE:ABC
00194   std::set<Hex*> result;
00195   std::string::size_type pos =s.find_first_of(":>");
00196   if(pos==std::string::npos)
00197       throw hex::invalid_argument(s);
00198   Hex* origin =this->hex( s.substr(0,pos) );
00199   Hex* start  =origin;
00200   while(pos!=std::string::npos)
00201   {
00202     std::string::size_type next =s.find_first_of(":>",pos+1);
00203     std::string steps =s.substr( pos+1, (next==s.npos)?(next):(next-pos-1) );
00204     if(s[pos]=='>')
00205     {
00206       start=origin->go(steps);
00207     }
00208     else // ':'
00209     {
00210       std::list<Hex*> hexes =Path(start,steps).hexes();
00211       std::copy(hexes.begin(), hexes.end(), inserter(result,result.end()));
00212       start=origin;
00213     }
00214     pos=next;
00215   }
00216   return result;
00217 }
00218 
00219 
00220 Path
00221 Grid::path(const std::string& s) const throw(out_of_range,invalid_argument)
00222 {
00223   std::string::size_type colon =s.find(':');
00224   if(colon==std::string::npos || (colon+1)>=s.size())
00225       throw hex::invalid_argument(s);
00226   Hex* origin =this->hex( s.substr(0,colon) );
00227   Path result(origin,s.substr(colon+1));
00228   return result;
00229 }
00230 
00231 
00232 Boundary
00233 Grid::boundary(const std::string& s) const throw(out_of_range,invalid_argument)
00234 {
00235   std::list<Edge*> result;
00236   std::string::size_type plus_minus =s.find_first_of("+-");
00237   if(plus_minus==std::string::npos || (plus_minus+1)>=s.size())
00238       throw hex::invalid_argument(s);
00239   bool clockwise =( '-' == s[plus_minus] );
00240   for(std::string::size_type pos =plus_minus+1; pos<s.size(); ++pos)
00241   {
00242     Direction d =to_direction(s[pos]);
00243     Edge* next;
00244     if(result.empty())
00245     {
00246       next = this->hex(s.substr(0,plus_minus))->edge(d);
00247     }
00248     else
00249     {
00250       next = result.back()->next_in(clockwise);
00251       if(next->direction() != d)
00252       {
00253         next = result.back()->next_out(clockwise);
00254         if(next->direction() != d)
00255             throw hex::invalid_argument(s);
00256       }
00257     }
00258     result.push_back(next);
00259   }
00260   return result;
00261 }
00262 
00263 
00264 Grid::Grid(int cols, int rows) throw(hex::out_of_range)
00265   : _hexes(), _cols(cols), _rows(rows)
00266 {
00267   // Perhaps it's perverse to use ints, and then restrict them to be only
00268   // short. I think it just makes the interface less quirky, the code a
00269   // bit more efficient, and using shorts wouldn't really save much memory.
00270   if(0>cols || cols>=0x4000)
00271       throw hex::out_of_range("cols");
00272   if(0>rows || rows>=0x4000)
00273       throw hex::out_of_range("rows");
00274 }
00275 
00276 
00277 Grid::Grid(const Grid& v): _hexes(), _cols(v._cols), _rows(v._rows)
00278 {
00279   using namespace std;
00280   for(map<int,Hex*>::const_iterator h =v._hexes.begin(); h!=v._hexes.end(); ++h)
00281       _hexes.insert( make_pair(h->first,new Hex(*this,*h->second)) );
00282 }
00283 
00284 
00285 Grid::~Grid()
00286 {
00287   using namespace std;
00288   for(map<int,Hex*>::const_iterator h =_hexes.begin(); h!=_hexes.end(); ++h)
00289       delete h->second;
00290 }
00291 
00292 
00293 } // end namespace hex

Generated on Thu Feb 21 00:00:54 2008 for libhex by  doxygen 1.5.1