/* -*- C++ -*- * Copyright ©2005 Hugo Mills * * This software is distributed under the terms of the GNU GPL v3 * For more information on the GPL, see the file COPYING or * visit http://www.gnu.org/ * * This software is distributed without warranty */ #include "datablock.h" #include #include #include #include #include #include #include #include #include DataBlock::DataBlock(const std::string& bn) : _vc(0), next_x(0), next_y(0), next_phase(0) { std::string base_name = bn + ".HDR"; std::ifstream meta(base_name.c_str()); while(!meta.eof()) { std::string s; meta >> s; if(s == "NROWS") { meta >> s; _height = atoi(s.c_str()); } else if (s == "NCOLS") { meta >> s; _width = atoi(s.c_str()); } } base_name = bn + ".DEM"; datafile = fopen(base_name.c_str(), "r"); struct stat status; int fd = fileno(datafile); fstat(fd, &status); length = status.st_size; _data = (char*)( mmap(0, length, PROT_READ, MAP_SHARED, fd, 0) ); _visited_nsedges = new char[_width * _height]; _visited_ewedges = new char[_width * _height]; _visited_corners = new char[_width * _height]; if(_visited_nsedges == NULL || _visited_ewedges == NULL || _visited_corners == NULL) { std::cerr << "Failed to allocate memory" << std::endl; exit(1); } for(int i=0; i<_width*_height; i++) { _visited_nsedges[i] = _visited_ewedges[i] = _visited_corners[i] = 0; } _visitmax = _width*_height*3-_width-_height; } DataBlock::~DataBlock() { munmap(_data, length); fclose(datafile); } void DataBlock::visit(const Crossing& e, bool v) { if(!valid(e)) return; size_t pos = e._pos.y*_width + e._pos.x; if(e._isc) { if(!_visited_corners[pos]) _vc++; if(v) _visited_corners[pos]++; else _visited_corners[pos]--; } else { switch(e._d) { case WEST: if(e._pos.x == 0) return; pos -= 1; // Fall through deliberate case EAST: if(!_visited_ewedges[pos]) _vc++; _visited_ewedges[pos] = v; break; case NORTH: if(e._pos.y == 0) return; pos -= _height; // Fall-through deliberate case SOUTH: if(!_visited_nsedges[pos]) _vc++; _visited_nsedges[pos] = v; break; } } if(_vc%500000 == 0) { std::cerr << '\r' << _vc << "/" << _visitmax << " (" << double(_vc)/double(_visitmax)*100 << "%)" << std::flush; } } bool DataBlock::visited(const Crossing& e) const { if(!valid(e)) return true; size_t pos = e._pos.y*_width + e._pos.x; if(e._isc) return _visited_corners[pos] > 4; else { switch(e._d) { case WEST: if(e._pos.x == 0) return true; pos -= 1; // Fall-through deliberate case EAST: return _visited_ewedges[pos]; break; case NORTH: if(e._pos.y == 0) return true; pos -= _height; // Fall-through deliberate case SOUTH: return _visited_nsedges[pos]; break; } } } bool DataBlock::valid(const Crossing& e) const { if(e._isc) { if(e._pos.x < 0 || e._pos.x >= _width) return false; if(e._pos.y < 0 || e._pos.y >= _height) return false; } else { if(e._pos.x < 0 || e._pos.x < 1 && e._d == 0) return false; if(e._pos.x >= _width || e._pos.x >= _width-1 && e._d == 2) return false; if(e._pos.y < 0 || e._pos.y < 1 && e._d == 1) return false; if(e._pos.y >= _height || e._pos.y >= _width-1 && e._d == 3) return false; } return true; } signed short int DataBlock::data(const iCoordinate& ic) const { if(!valid(ic)) return -19999; unsigned char* bytepos = (unsigned char*)(_data + ((ic.y*_width + ic.x) * 2)); // We need to swab on this file: it's LSB first return (bytepos[0] << 8) | bytepos[1]; } Crossing DataBlock::next(int height) { if(next_phase == 0) { for(; next_y<_height; next_y++) { for(; next_x<_width-1; next_x++) { Crossing c(next_x, next_y, EAST); if(visited(c)) continue; visit(c); if(crossed(c, height)) return c; } next_x = 0; } next_phase++; next_y = 0; } if(next_phase == 1) { for(; next_y<_height-1; next_y++) { for(; next_x<_width; next_x++) { Crossing c(next_x, next_y, SOUTH); if(visited(c)) continue; visit(c); if(crossed(c, height)) return c; } next_x = 0; } next_phase++; next_y = 0; } if(next_phase == 2) { for(; next_y<_height; next_y++) { for(; next_x<_width; next_x++) { Crossing c(next_x, next_y); if(visited(c)) continue; visit(c); if(crossed(c, height)) return c; } next_x = 0; } next_phase++; next_y = 0; } return Crossing(); } Coordinate DataBlock::intercept(const Crossing& e, int _z) const { if(e._isc) return Coordinate(double(e._pos.x), double(e._pos.y)); else { iCoordinate a = e.start(); iCoordinate b = e.end(); double ha = double(data(a)); double hb = double(data(b)); double f = (_z-ha)/(hb-ha); double g = 1-f; return Coordinate( a.x*g + b.x*f, a.y*g + b.y*f ); } } bool DataBlock::crossed(const Crossing& e, int z) const { if(e._isc) return data(e.pos()) == z; else { int h1 = data(e.start()); int h2 = data(e.end()); if(h1 < z && h2 > z) return true; if(h1 > z && h2 < z) return true; return false; } } void DataBlock::dump_local(const iCoordinate& cnt) const { std::cerr << " "; for(long x = std::max(cnt.x - 3, 0); x < std::min(cnt.x + 4, width()); x++) { std::cerr << std::setw(5) << x << " "; } std::cerr << std::endl; for(long y = std::max(cnt.y - 3, 0); y < std::min(cnt.y + 4, height()); y++) { std::cerr << std::setw(6) << y << ": "; for(long x = std::max(cnt.x - 3, 0); x < std::min(cnt.x + 4, width()); x++) { std::cerr << std::setw(5); std::cerr << data(iCoordinate(x, y)) << " "; if(visited(Crossing(x, y, EAST))) std::cerr << "-"; else std::cerr << " "; } std::cerr << std::endl << " "; for(long x = std::max(cnt.x - 3, 0); x < std::min(cnt.x + 4, width()); x++) { std::cerr << " "; if(visited(Crossing(x, y, SOUTH))) std::cerr << "| "; else std::cerr << " "; } std::cerr << std::endl; } }