All Classes Namespaces Files Functions Variables Enumerations Enumerator
programs/UrgScanner/UrgDataDraw.cpp
Go to the documentation of this file.
00001 
00012 #include <QMouseEvent>
00013 #include <cmath>
00014 #include <deque>
00015 #include "UrgDataDraw.h"
00016 #include "RangeSensor.h"
00017 #include "ticks.h"
00018 #include <cstdio>
00019 
00020 using namespace qrk;
00021 using namespace std;
00022 
00023 
00024 namespace
00025 {
00026   typedef vector<Point3d<int> > Points;
00027 
00028   typedef struct
00029   {
00030     Points points;
00031     qrk::Point3d<int> rotate;
00032     int timestamp;
00033     vector<long> intensity;
00034   } Line;
00035 
00036   typedef deque<Line> Lines;
00037 }
00038 
00039 
00040 struct UrgDataDraw::pImpl
00041 {
00042   enum {
00043     //AliveMsec = 3000,           // [msec]
00044     AliveMsec = 20,             // [msec]
00045   };
00046 
00047   QColor clear_color_;
00048   vector<long> data_;
00049   vector<long> intensity_data_;
00050   size_t data_max_;
00051   long length_min_;
00052   long length_max_;
00053 
00054   Lines saved_lines_data_;
00055   Lines normal_lines_data_;
00056   Line recent_line_data_;
00057 
00058   QPoint last_pos_;
00059   int x_rot_;
00060   int y_rot_;
00061   int z_rot_;
00062 
00063   bool h_type_;
00064   bool front_only_;
00065   bool pre_record_;
00066 
00067   int magnify_;
00068   bool no_plot_;
00069   bool intensity_mode_;
00070 
00071   vector<Point3d<double> > color_table_;
00072 
00073 
00074   pImpl(void)
00075     : clear_color_(Qt::black), data_max_(0),
00076       length_min_(0), length_max_(0),
00077       x_rot_(0), y_rot_(0), z_rot_(0),
00078       h_type_(false), front_only_(false), pre_record_(false),
00079       magnify_(50),
00080       no_plot_(false), intensity_mode_(false)
00081   {
00082     // 色変換テーブルの初期化
00083     for (int mm_height = 0; mm_height < 1000; ++mm_height) {
00084       QColor color;
00085       color.setHsv(static_cast<int>(360.0 * mm_height / 1000.0), 255, 255);
00086 
00087       int r, g, b;
00088       color.getRgb(&r, &g, &b);
00089       color_table_.push_back(Point3d<double>(r / 255.0, g / 255.0, b / 255.0));
00090     }
00091   }
00092 
00093 
00094   void initializeForm(UrgDataDraw* parent)
00095   {
00096     static_cast<void>(parent);
00097     parent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00098   }
00099 
00100 
00101   void initializeGL(UrgDataDraw* parent)
00102   {
00103     parent->qglClearColor(clear_color_);
00104     glEnable(GL_DEPTH_TEST);
00105     glEnable(GL_CULL_FACE);
00106     glEnable(GL_TEXTURE_2D);
00107   }
00108 
00109 
00110   void paintGL(UrgDataDraw* parent)
00111   {
00112     parent->qglClearColor(clear_color_);
00113     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00114 
00115     glLoadIdentity();
00116 
00117     glRotated(x_rot_ / 16.0, 1.0, 0.0, 0.0);
00118     glRotated(y_rot_ / 16.0, 0.0, 1.0, 0.0);
00119     glRotated((z_rot_ / 16.0) + 90, 0.0, 0.0, 1.0);
00120 
00121     // 古いデータの削除
00122     while ((! normal_lines_data_.empty()) &&
00123            ((normal_lines_data_.front().timestamp + AliveMsec) < ticks())) {
00124       normal_lines_data_.pop_front();
00125     }
00126 
00127     glBegin(GL_POINTS);
00128 
00129     // 描画の拡大率
00130     double ratio = (1.0 / 2.0) + (5.0 * magnify_ / 100.0);
00131 
00132     // 通常データ
00133     if (! no_plot_) {
00134       for (Lines::iterator line_it = normal_lines_data_.begin();
00135            line_it != normal_lines_data_.end(); ++line_it) {
00136         // !!! false をマクロに置き換える
00137         drawLine(line_it, false, ratio);
00138       }
00139     }
00140 
00141     // 記録データ
00142     for (Lines::iterator line_it = saved_lines_data_.begin();
00143          line_it != saved_lines_data_.end(); ++line_it) {
00144       drawLine(line_it, true, ratio);
00145     }
00146 
00147     if (! no_plot_) {
00148       // 最新データの点に対して、レーザを描画する
00149       drawLaser(recent_line_data_, ratio);
00150     }
00151     glEnd();
00152   }
00153 
00154   void drawLaser(Line& line, double ratio)
00155   {
00156     glColor3d(0.6, 0.0, 0.0);
00157 
00158     int index = 0;
00159     for (Points::iterator it = line.points.begin();
00160          it != line.points.end(); ++it, ++index) {
00161 
00162       if ((it->x == 0) && (it->y == 0) && (it->z == 0)) {
00163         continue;
00164       }
00165 
00166       if ((index & 0x3) == 0x00) {
00167         glBegin(GL_LINE_STRIP);
00168         glVertex3d(0.0, 0.0, 0.0);
00169         glVertex3d(it->x * ratio, it->y * ratio, it->z * ratio);
00170         glEnd();
00171       }
00172     }
00173   }
00174 
00175 
00176   void drawLine(Lines::iterator line, bool record, double ratio)
00177   {
00178     if (! record) {
00179       // 時間に応じた色分け
00180       double gradation =
00181         1.0 * (AliveMsec - (ticks() - line->timestamp)) / AliveMsec;
00182       glColor3d(1.0 * gradation, 1.0 * gradation, 1.0 * gradation);
00183 
00184     } else {
00185       // 通常時の色
00186       glColor3d(0.0, 1.0, 0.0);
00187     }
00188 
00189     int index = 0;
00190     for (Points::iterator it = line->points.begin();
00191          it != line->points.end(); ++it, ++index) {
00192 
00193       if (record) {
00194         if (intensity_mode_) {
00195           // 強度情報に応じた色分け
00196           //int ratio = static_cast<int>((line->intensity[index] % 4000) / 4.0);
00197           int ratio = (line->intensity[index] + 1000) % 1000;
00198           Point3d<double>& color = color_table_[ratio];
00199           glColor3d(color.x, color.y, color.z);
00200 
00201         } else {
00202 
00203           // 記録中は、奥行きによって色を変える
00204           int mm = ((it->x % 1000) + 1000) % 1000;
00205           Point3d<double>& color = color_table_[mm];
00206           glColor3d(color.x, color.y, color.z);
00207         }
00208       }
00209       glVertex3d(it->x * ratio, it->y * ratio, it->z * ratio);
00210     }
00211   }
00212 
00213 
00214   void convertScanData(Line& line, RangeSensor& sensor)
00215   {
00216     data_max_ = sensor.maxScanLines();
00217     length_min_ = sensor.minDistance();
00218     length_max_ = sensor.maxDistance();
00219 
00220     line.timestamp = ticks();
00221     int n = sensor.capture(data_, NULL);
00222     if (n <= 0) {
00223       return;
00224     }
00225 
00226     // 強度データの取得
00227     int intensity_n = sensor.captureWithIntensity(data_, intensity_data_,
00228                                                   NULL);
00229     if (intensity_n == 0) {
00230       // 強度データが返されないならば、モードを無効とみなす
00231       intensity_mode_ = false;
00232     }
00233 
00234     // 測距データを2次元展開してから、Wii の向きに応じてさらに回転させる
00235     for (int i = 0; i < n; ++i) {
00236       long length = data_[i];
00237       if ((length <= length_min_) || (length >= length_max_)) {
00238         continue;
00239       }
00240 
00241       int index = i;
00242       double radian = sensor.index2rad(index);
00243       if (front_only_ && (fabs(radian) > M_PI / 2.0)) {
00244         // 前面以外のデータは無視する
00245         continue;
00246       }
00247 
00248       Point3d<int> p;
00249       if (! h_type_) {
00250         p.x = static_cast<int>(length * cos(radian));
00251         p.y = static_cast<int>(length * sin(radian));
00252         p.z = 0;
00253       } else {
00254         p.x = 0;
00255         p.y = -static_cast<int>(length * cos(radian));
00256         p.z = -static_cast<int>(length * sin(radian));
00257       }
00258 
00259       if (h_type_) {
00260         adjustTypeH(p, static_cast<int>(180.0 * radian / M_PI));
00261       }
00262 
00263       // wiimote の角度に基づいた回転演算を行う
00264       rotateX(p, line.rotate.x);
00265       rotateY(p, line.rotate.y);
00266       rotateY(p, line.rotate.z);
00267 
00268       line.points.push_back(p);
00269       if (intensity_n > index) {
00270         line.intensity.push_back(intensity_data_[index]);
00271       }
00272     }
00273   }
00274 
00275 
00276   void adjustTypeH(Point3d<int>& p, int degree)
00277   {
00278     if ((degree > -20) && (degree < 20)) {
00279       // 前方は、Z 軸に対して、90 度ほど回転
00280       //rotateZ(p, +90);
00281       // !!! なぜか、-90 度にすると、適切に動作する?
00282       rotateZ(p, -90);
00283 
00284       // さらに、Y 軸の負方向に移動して、X 軸の負方向に移動させる
00285       // !!!
00286 
00287       //p = Point3d<int>(0, 0, 0);
00288 
00289     } else if (degree < -45) {
00290       // 右側は、Y 軸に対して、+60 度ほど回転
00291       rotateY(p, +(60 + 15 + 0));
00292 
00293       // さらに、Z 軸の正方向に移動して、X 軸の負方向に移動させる(誤差無視)
00294       // !!!
00295 
00296     } else if (degree > +45) {
00297       // 左側は、Y 軸に対して、-60 度ほど回転
00298       rotateY(p, -(60 + 15 + 0));
00299 
00300       // さらに、Z 軸の負方向に移動して、X 軸の負方向に移動させる(誤差無視)
00301       // !!!
00302     } else {
00303       // それ以外は、今回は利用しない
00304       p = Point3d<int>(0, 0, 0);
00305     }
00306   }
00307 
00308 
00309   void rotateX(Point3d<int>& point, int rotate_degree)
00310   {
00311     double radian = rotate_degree * M_PI / 180.0;
00312     double z2 = (point.z * cos(-radian)) - (point.y * sin(-radian));
00313     double y2 = (point.z * sin(-radian)) + (point.y * cos(-radian));
00314 
00315     point.z = static_cast<int>(z2);
00316     point.y = static_cast<int>(y2);
00317   }
00318 
00319 
00320   void rotateY(Point3d<int>& point, int rotate_degree)
00321   {
00322     double radian = -rotate_degree * M_PI / 180.0;
00323     double z2 = (point.z * cos(-radian)) - (point.x * sin(-radian));
00324     double x2 = (point.z * sin(-radian)) + (point.x * cos(-radian));
00325 
00326     point.z = static_cast<int>(z2);
00327     point.x = static_cast<int>(x2);
00328   }
00329 
00330 
00331   void rotateZ(Point3d<int>& point, int rotate_degree)
00332   {
00333     double radian = rotate_degree * M_PI / 180.0;
00334     double x2 = (point.x * cos(-radian)) - (point.y * sin(-radian));
00335     double y2 = (point.x * sin(-radian)) + (point.y * cos(-radian));
00336 
00337     point.x = static_cast<int>(x2);
00338     point.y = static_cast<int>(y2);
00339   }
00340 
00341 
00342   void addSaveLine(Line& line)
00343   {
00344     saved_lines_data_.push_back(line);
00345   }
00346 
00347 
00348   void addTemporaryLine(Line& line)
00349   {
00350     normal_lines_data_.push_back(line);
00351   }
00352 
00353 
00354   void setXRotation(UrgDataDraw* parent, int angle)
00355   {
00356     normalizeAngle(&angle);
00357     if (angle != x_rot_) {
00358       x_rot_ = angle;
00359       parent->updateGL();
00360     }
00361   }
00362 
00363 
00364   void setYRotation(UrgDataDraw* parent, int angle)
00365   {
00366     normalizeAngle(&angle);
00367     if (angle != y_rot_) {
00368       y_rot_ = angle;
00369       parent->updateGL();
00370     }
00371   }
00372 
00373 
00374   void setZRotation(UrgDataDraw* parent, int angle)
00375   {
00376     normalizeAngle(&angle);
00377     if (angle != z_rot_) {
00378       z_rot_ = angle;
00379       parent->updateGL();
00380     }
00381   }
00382 
00383 
00384   void normalizeAngle(int *angle)
00385   {
00386     while (*angle < 0) {
00387       *angle += 360 * 16;
00388     }
00389 
00390     while (*angle > 360 * 16) {
00391       *angle -= 360 * 16;
00392     }
00393   }
00394 };
00395 
00396 
00397 UrgDataDraw::UrgDataDraw(QWidget* parent)
00398   : QGLWidget(parent), pimpl(new pImpl)
00399 {
00400   pimpl->initializeForm(this);
00401 }
00402 
00403 
00404 UrgDataDraw::~UrgDataDraw(void)
00405 {
00406 }
00407 
00408 
00409 void UrgDataDraw::initializeGL(void)
00410 {
00411   pimpl->initializeGL(this);
00412 }
00413 
00414 
00415 void UrgDataDraw::resizeGL(int width, int height)
00416 {
00417   glViewport(0, 0, width, height);
00418 
00419   glMatrixMode(GL_PROJECTION);
00420   glLoadIdentity();
00421 
00422   double aspect = 1.0 * width / height;
00423   glOrtho(-5000 * aspect, 5000 * aspect, -5000, 5000, -100000, 100000);
00424 
00425   glMatrixMode(GL_MODELVIEW);
00426 }
00427 
00428 
00429 void UrgDataDraw::paintGL(void)
00430 {
00431   pimpl->paintGL(this);
00432 }
00433 
00434 
00435 QSize UrgDataDraw::minimumSizeHint(void) const
00436 {
00437   return QSize(150, 150);
00438 }
00439 
00440 
00441 void UrgDataDraw::redraw(RangeSensor& sensor,
00442                          Point3d<int>& wii_rotate, bool record, bool no_plot)
00443 {
00444   //fprintf(stderr, "redraw.\n");
00445 
00446   // 取得中のデータを表示するか、の設定を更新する
00447   pimpl->no_plot_ = no_plot;
00448 
00449   // 距離データの取得と3次元変換
00450   Line line;
00451   line.rotate = wii_rotate;
00452   pimpl->convertScanData(line, sensor);
00453 
00454   if (record) {
00455     if (pimpl->pre_record_ == false) {
00456       // 新しい記録が開始されたら、前回の記録データを削除
00457       pimpl->saved_lines_data_.clear();
00458     }
00459     // 描画データとして登録
00460     pimpl->addSaveLine(line);
00461 
00462   } else {
00463     // 一時データとして登録
00464     pimpl->addTemporaryLine(line);
00465   }
00466 
00467   // 最新データをレーザ表示用に登録
00468   if (! line.points.empty()) {
00469     swap(pimpl->recent_line_data_, line);
00470   }
00471 
00472   pimpl->pre_record_ = record;
00473 
00474   updateGL();
00475 }
00476 
00477 
00478 void UrgDataDraw::clearCaptureData(void)
00479 {
00480   pimpl->normal_lines_data_.clear();
00481   pimpl->recent_line_data_.points.clear();
00482   update();
00483 }
00484 
00485 
00486 void UrgDataDraw::mousePressEvent(QMouseEvent *event)
00487 {
00488   pimpl->last_pos_ = event->pos();
00489 }
00490 
00491 
00492 void UrgDataDraw::mouseMoveEvent(QMouseEvent *event)
00493 {
00494   int dx = event->x() - pimpl->last_pos_.x();
00495   int dy = event->y() - pimpl->last_pos_.y();
00496 
00497   if (event->buttons() & Qt::LeftButton) {
00498     pimpl->setXRotation(this, pimpl->x_rot_ + 8 * dy);
00499     pimpl->setZRotation(this, pimpl->z_rot_ + 8 * dx);
00500 
00501   } else if (event->buttons() & Qt::RightButton) {
00502     pimpl->setXRotation(this, pimpl->x_rot_ + 8 * dy);
00503     pimpl->setYRotation(this, pimpl->y_rot_ + 8 * dx);
00504   }
00505   pimpl->last_pos_ = event->pos();
00506 }
00507 
00508 
00509 void UrgDataDraw::setTypeH(bool checked)
00510 {
00511   pimpl->h_type_ = checked;
00512 }
00513 
00514 
00515 void UrgDataDraw::setFrontOnly(bool front_only)
00516 {
00517   pimpl->front_only_ = front_only;
00518 }
00519 
00520 
00521 void UrgDataDraw::magnifyChanged(int value)
00522 {
00523   pimpl->magnify_ = value;
00524   update();
00525 }
00526 
00527 
00528 void UrgDataDraw::resetView(void)
00529 {
00530   pimpl->x_rot_ = 0;
00531   pimpl->y_rot_ = 0;
00532   pimpl->z_rot_ = 0;
00533 }
00534 
00535 
00536 void UrgDataDraw::loadVrml(const string& fileName)
00537 {
00538   // !!! 余力あらば、Qt API で書き直す
00539 
00540   FILE* fd = fopen(fileName.c_str(), "r");
00541   if (fd == NULL) {
00542     return;
00543   }
00544 
00545   enum { BufferMax = 256 };
00546   char buffer[BufferMax];
00547 
00548   bool in_data = false;
00549 
00550   Line line_data;
00551   while (fgets(buffer, BufferMax, fd) != NULL) {
00552 
00553     if (strstr(buffer, "end") != NULL) {
00554       // "end" を見付けたら、おしまい
00555       break;
00556     }
00557 
00558     if (in_data) {
00559       double x, y, z;
00560       if (sscanf(buffer, "%lf %lf %lf", &x, &y, &z) == 3) {
00561         Point3d<int> point(static_cast<int>(x * 1000.0),
00562                           static_cast<int>(y * 1000.0),
00563                           static_cast<int>(z * 1000.0));
00564         line_data.points.push_back(point);
00565       }
00566     }
00567 
00568     if (strstr(buffer, "begin") != NULL) {
00569       // "begin" を見付けたら、データ取得モードとする
00570       in_data = true;
00571     }
00572   }
00573 
00574   pimpl->saved_lines_data_.clear();
00575   pimpl->saved_lines_data_.push_back(line_data);
00576 }
00577 
00578 
00579 void UrgDataDraw::saveVrml(const string& fileName)
00580 {
00581   // !!! いずれ、Qt API で書き直す
00582 
00583   const char header[] =
00584     "#VRML V2.0 utf8\n"
00585     "Shape\n"
00586     "{\n"
00587     "  geometry PointSet\n"
00588     "  {\n";
00589 
00590   const char coord_header[] =
00591     "    coord Coordinate\n"
00592     "    {\n";
00593 
00594   const char point_header[] =
00595     "      point\n"
00596     "      [\n";
00597 
00598   const char point_footer[] =
00599     "      ]\n";
00600 
00601   const char coord_footer[] =
00602     "    }\n";
00603 
00604   const char color_header[] =
00605     "    color Color\n"
00606     "    {\n"
00607     "      color\n"
00608     "      [\n";
00609 
00610   const char color_footer[] =
00611     "      ]\n"
00612     "    }\n";
00613 
00614   const char footer[] =
00615     "  }\n"
00616     "}\n";
00617 
00618   FILE* fd = fopen(fileName.c_str(), "w");
00619   if (fd == NULL) {
00620     return;
00621   }
00622 
00623   // ヘッダ部の出力
00624   fprintf(fd, "%s", header);
00625 
00626   // 点列データの出力
00627   fprintf(fd, "%s", coord_header);
00628   fprintf(fd, "%s", point_header);
00629   fprintf(fd, "        # begin points.\n");
00630   for (Lines::iterator line_it = pimpl->saved_lines_data_.begin();
00631        line_it != pimpl->saved_lines_data_.end(); ++line_it) {
00632     for (Points::iterator it = line_it->points.begin();
00633          it != line_it->points.end(); ++it) {
00634 
00635       fprintf(fd, "        %.3f %.3f %.3f\n",
00636               it->x / 1000.0, it->y / 1000.0, it->z / 1000.0);
00637     }
00638   }
00639   fprintf(fd, "        # end points.\n");
00640   fprintf(fd, "%s", point_footer);
00641   fprintf(fd, "%s", coord_footer);
00642 
00643   // 色データの出力
00644   fprintf(fd, "%s", color_header);
00645   for (Lines::iterator line_it = pimpl->saved_lines_data_.begin();
00646        line_it != pimpl->saved_lines_data_.end(); ++line_it) {
00647     for (Points::iterator it = line_it->points.begin();
00648          it != line_it->points.end(); ++it) {
00649 
00650       int mm = ((it->z % 1000) + 1000) % 1000;
00651       Point3d<double>& color = pimpl->color_table_[mm];
00652       fprintf(fd, "        %f %f %f,\n", color.x, color.y, color.z);
00653     }
00654   }
00655   fprintf(fd, "%s", color_footer);
00656 
00657   // フッタ部の出力
00658   fprintf(fd, "%s", footer);
00659   fclose(fd);
00660 }
00661 
00662 
00663 void UrgDataDraw::setIntensityMode(bool on)
00664 {
00665   pimpl->intensity_mode_ = on;
00666 }