beams.cpp

00001
00002 /***************************************************************************
00003  *  beams.h - Scanline model implementation: beams
00004  *
00005  *  Created: Tue Apr 17 21:09:46 2007
00006  *  Copyright  2005-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023
00024 #include <core/exception.h>
00025 #include <models/scanlines/beams.h>
00026
00027 #include <cmath>
00028
00029 using fawkes::point_t;
00030 
00031 /** @class ScanlineBeams <models/scanlines/beams.h>
00032  * Raytraced beams scanline model.
00033  * This model uses a defined number of beams shot from the bottom of the image
00034  * towards the top using Bresenham. With this you can have kind of a radar-like
00035  * scanline model. Additionally the starting points at the bottom can be
00036  * distributed over the full width of the image which alles for a scan aligned
00037  * to the image.
00038  *
00039  * To ease the calculation of the finished state the very last point is traversed
00040  * twice.
00041  *
00042  * @author Tim Niemueller
00043  */
00044 
00045 /** Construtor.
00046  * @param image_width image width
00047  * @param image_height image height
00048  * @param start_x x coordinate of the starting point, ignored if distributed (see below)
00049  * @param start_y y coordinate of the starting point, this is the lowest points of the
00050  * the lines and should thus be close to the bottom of the image
00051  * @param stop_y Y coordinate for stopping the traversal
00052  * @param offset_y number of pixel to advance in Y-direction per iteration
00053  * @param distribute_start_x set to true, to distribute the start x coordinates
00054  * equidistant over the whole width of the image.
00055  * @param angle_from angle to start the scan at, a straight vertical line means
00056  * zero rad, clock-wise positive, in radians
00057  * @param angle_range the range to use to distribute the beams, clockwise positive,
00058  * in radians
00059  * @param num_beams number of beams to use
00060  * @exception Exception thrown if parameters are out of bounds
00061  */
00062 ScanlineBeams::ScanlineBeams(unsigned int image_width, unsigned int image_height,
00063                              unsigned int start_x, unsigned int start_y,
00064                              unsigned int stop_y, unsigned int offset_y,
00065                              bool distribute_start_x,
00066                              float angle_from, float angle_range,
00067                              unsigned int num_beams)
00068 {
00069   if ( start_y < stop_y )  throw fawkes::Exception("start_y < stop_y");
00070   if ( (stop_y > image_height) || (start_y > image_height) ) {
00071     throw fawkes::Exception("(stop_y > height) || (start_y > height)");
00072   }
00073
00074   this->start_x = start_x;
00075   this->start_y = start_y;
00076   this->angle_from = angle_from;
00077   this->angle_range = angle_range;
00078   this->num_beams = num_beams;
00079   this->stop_y = stop_y;
00080   this->offset_y = offset_y;
00081   this->image_width = image_width;
00082   this->image_height = image_height;
00083   this->distribute_start_x = distribute_start_x;
00084
00085   reset();
00086 }
00087
00088
00089 point_t
00090 ScanlineBeams::operator*()
00091 {
00092   return coord;
00093 }
00094
00095 point_t*
00096 ScanlineBeams::operator->()
00097 {
00098   return &coord;
00099 }
00100
00101
00102 bool
00103 ScanlineBeams::finished()
00104 {
00105   return _finished;
00106 }
00107
00108
00109 void
00110 ScanlineBeams::advance()
00111 {
00112
00113   while ( ! _finished && (first_beam < last_beam) ) {
00114
00115     unsigned int x_start = beam_current_pos[next_beam].x;
00116     unsigned int y_start = beam_current_pos[next_beam].y;
00117
00118     unsigned int x_end = beam_end_pos[next_beam].x;
00119     unsigned int y_end = beam_end_pos[next_beam].y;
00120
00121     int x, y, dist, xerr, yerr, dx, dy, incx, incy;
00122
00123     // calculate distance in both directions
00124     dx = x_end - x_start;
00125     dy = y_end - y_start;
00126
00127     // Calculate sign of the increment
00128     if(dx < 0) {
00129       incx = -1;
00130       dx = -dx;
00131     } else {
00132       incx = dx ? 1 : 0;
00133     }
00134
00135     if(dy < 0) {
00136       incy = -1;
00137       dy = -dy;
00138     } else {
00139       incy = dy ? 1 : 0;
00140     }
00141
00142     // check which distance is larger
00143     dist = (dx > dy) ? dx : dy;
00144
00145     // Initialize for loops
00146     x = x_start;
00147     y = y_start;
00148     xerr = dx;
00149     yerr = dy;
00150
00151     /* Calculate and draw pixels */
00152     unsigned int offset = 0;
00153     while ( (x >= 0) && ((unsigned int )x < image_width) && ((unsigned int)y > stop_y) &&
00154             (offset < offset_y) ) {
00155       ++offset;
00156
00157       xerr += dx;
00158       yerr += dy;
00159
00160       if(xerr > dist) {
00161         xerr -= dist;
00162         x += incx;
00163       }
00164
00165       if(yerr>dist) {
00166         yerr -= dist;
00167         y += incy;
00168       }
00169     }
00170     if ( (y < 0) || (unsigned int)y <= stop_y ) {
00171       _finished = true;
00172       break;
00173     }
00174     if ( x < 0 ) {
00175       first_beam = ++next_beam;
00176       continue;
00177     }
00178     if ( (unsigned int)x > image_width ) {
00179       last_beam = next_beam - 1;
00180       next_beam = first_beam;
00181       continue;
00182     }
00183
00184     coord.x = x;
00185     coord.y = y;
00186
00187     beam_current_pos[next_beam] = coord;
00188
00189     if ( next_beam < last_beam) {
00190       ++next_beam;
00191     } else {
00192       next_beam = first_beam;
00193     }
00194     break;
00195   }
00196
00197 }
00198
00199
00200 point_t *
00201 ScanlineBeams::operator++()
00202 {
00203   advance();
00204   return &coord;
00205 }
00206
00207
00208 point_t *
00209 ScanlineBeams::operator++(int i)
00210 {
00211   tmp_coord.x = coord.x;
00212   tmp_coord.y = coord.y;
00213   advance();
00214   return &tmp_coord;
00215 }
00216
00217
00218 void
00219 ScanlineBeams::reset()
00220 {
00221   _finished = false;
00222
00223   beam_current_pos.clear();
00224   if ( distribute_start_x ) {
00225     unsigned int offset_start_x = image_width / (num_beams - 1);
00226     for (unsigned int i = 0; i < num_beams; ++i) {
00227       coord.x = i * offset_start_x;
00228       coord.y = start_y;
00229       beam_current_pos.push_back(coord);
00230     }
00231     coord.x = beam_current_pos[0].x;
00232     coord.y = beam_current_pos[0].y;
00233   } else {
00234     coord.x = start_x;
00235     coord.y = start_y;
00236     beam_current_pos.resize( num_beams, coord );
00237   }
00238
00239
00240   beam_end_pos.clear();
00241   next_beam = 0;
00242   float angle_between_beams = angle_range / num_beams;
00243   for (unsigned int i = 0; i < num_beams; ++i) {
00244     float diff_y = beam_current_pos[i].y - stop_y;
00245     float diff_x = diff_y * tan( angle_from + (float)i * angle_between_beams );
00246     point_t end_point;
00247     end_point.y = stop_y;
00248     end_point.x = (int)roundf(diff_x) + start_x;
00249     beam_end_pos.push_back(end_point);
00250   }
00251   first_beam = 0;
00252   last_beam = beam_end_pos.size() - 1;
00253 }
00254
00255 const char *
00256 ScanlineBeams::get_name()
00257 {
00258   return "ScanlineModel::Beams";
00259 }
00260
00261
00262 unsigned int
00263 ScanlineBeams::get_margin()
00264 {
00265   return offset_y;
00266 }