overlay_menu_display.cpp
Go to the documentation of this file.
00001 // -*- mode: c++; -*-
00002 /*********************************************************************
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2014, JSK Lab
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/o2r other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of the Willow Garage nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
00034  *********************************************************************/
00035 
00036 #include "overlay_menu_display.h"
00037 
00038 #include <OGRE/OgreMaterialManager.h>
00039 #include <OGRE/OgreTextureManager.h>
00040 #include <OGRE/OgreTexture.h>
00041 #include <OGRE/OgreHardwarePixelBuffer.h>
00042 #include <OGRE/OgreTechnique.h>
00043 
00044 #include <rviz/uniform_string_stream.h>
00045 #include <rviz/display_context.h>
00046 #include <rviz/view_manager.h>
00047 #include <rviz/render_panel.h>
00048 
00049 namespace jsk_rviz_plugin
00050 {
00051 
00052   const int menu_padding_x = 100;
00053   const int menu_padding_y = 5;
00054   const int menu_last_padding_y = 30;
00055   const double animate_duration = 0.2;
00056   OverlayMenuDisplay::OverlayMenuDisplay() : Display()
00057   {
00058     update_topic_property_ = new rviz::RosTopicProperty(
00059       "Topic", "",
00060       ros::message_traits::datatype<jsk_rviz_plugins::OverlayMenu>(),
00061       "jsk_rviz_plugins::OverlayMenu topic to subscribe to.",
00062       this, SLOT( updateTopic() ));
00063  
00064   }
00065 
00066   OverlayMenuDisplay::~OverlayMenuDisplay()
00067   {
00068     onDisable();
00069     delete update_topic_property_;
00070   }
00071 
00072   void OverlayMenuDisplay::onInitialize()
00073   {
00074     require_update_texture_ = false;
00075     animation_state_ = CLOSED;
00076   }
00077   
00078   void OverlayMenuDisplay::onEnable()
00079   {
00080     if (overlay_) {
00081       overlay_->show();
00082     }
00083     subscribe();
00084   }
00085   void OverlayMenuDisplay::onDisable()
00086   {
00087     if (overlay_) {
00088       overlay_->hide();
00089     }
00090     unsubscribe();
00091   }
00092 
00093   void OverlayMenuDisplay::unsubscribe()
00094   {
00095     sub_.shutdown();
00096   }
00097 
00098   void OverlayMenuDisplay::subscribe()
00099   {
00100     std::string topic_name = update_topic_property_->getTopicStd();
00101     if (topic_name.length() > 0 && topic_name != "/") {
00102       sub_ = ros::NodeHandle().subscribe(topic_name, 1,
00103                                          &OverlayMenuDisplay::processMessage,
00104                                          this);
00105     }
00106   }
00107 
00108   void OverlayMenuDisplay::processMessage
00109   (const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg)
00110   {
00111     next_menu_ = msg;
00112   }
00113 
00114   bool OverlayMenuDisplay::isNeedToResize()
00115   {
00116     if (!current_menu_ && next_menu_) { // first time
00117       ROS_DEBUG("need to resize because this is the first time to draw");
00118       return true;
00119     }
00120     else if (!current_menu_ && !next_menu_) {
00121       // both are null, it means that ...
00122       // the plugin tries to draw without message reception
00123       ROS_DEBUG("no need to resize because the plugin tries to draw without message reception");
00124       return false;
00125     }
00126     else if (current_menu_ && !next_menu_) {
00127       // this is unexpected case
00128       ROS_DEBUG("no need to resize, this is unexpected case. please debug");
00129       return false;
00130     }
00131     else {
00132       if (current_menu_->menus.size() != next_menu_->menus.size()) {
00133         ROS_DEBUG("need to resize because the length of menu is different");
00134         return true;
00135       }
00136       else {
00137         // check all the menu is same or not
00138         for (size_t i = 0; i < current_menu_->menus.size(); i++) {
00139           if (current_menu_->menus[i] != next_menu_->menus[i]) {
00140             ROS_DEBUG("need to resize because the content of menu is different");
00141             return true;
00142           }
00143         }
00144         ROS_DEBUG("no need to resize because the content of menu is same");
00145         return false;
00146       }
00147     }
00148   }
00149 
00150   QFont OverlayMenuDisplay::font()
00151   {
00152     QFont font;
00153     font.setPointSize(20);
00154     return font;
00155   }
00156   
00157   QFontMetrics OverlayMenuDisplay::fontMetrics()
00158   {
00159     QFontMetrics fm(font());
00160     return fm;
00161   }
00162   
00163   int OverlayMenuDisplay::drawAreaWidth(
00164     const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg)
00165   {
00166     QFontMetrics fm = fontMetrics();
00167     int max_width = 0;
00168     for (size_t i = 0; i < msg->menus.size(); i++) {
00169       int w = fm.width(getMenuString(msg, i).c_str());
00170       if (max_width < w) {
00171         max_width = w;
00172       }
00173     }
00174     return max_width + menu_padding_x * 2;
00175   }
00176 
00177   int OverlayMenuDisplay::drawAreaHeight(
00178     const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg)
00179   {
00180     QFontMetrics fm = fontMetrics();
00181     return fm.height() * (msg->menus.size() + 1)
00182       + menu_padding_y * (msg->menus.size() + 1 - 1)
00183       + menu_last_padding_y * 2;
00184   }
00185   
00186   void OverlayMenuDisplay::update(float wall_dt, float ros_dt)
00187   {
00188     if (!next_menu_) {
00189       ROS_DEBUG("next_menu_ is null, no need to update");
00190       return;
00191     }
00192     if (next_menu_->action == jsk_rviz_plugins::OverlayMenu::ACTION_CLOSE &&
00193         animation_state_ == CLOSED) {
00194       ROS_DEBUG("request is close and state is closed, we ignore it completely");
00195       return;
00196     }
00197     // if (!overlay_) {
00198     //   static int count = 0;
00199     //   rviz::UniformStringStream ss;
00200     //   ss << "OverlayMenuDisplayObject" << count++;
00201     //   overlay_.reset(new OverlayObject(ss.str()));
00202     //   overlay_->show();
00203     // }
00204     
00205     // if (isNeedToResize()) {
00206     //   overlay_->updateTextureSize(drawAreaWidth(next_menu_), drawAreaHeight(next_menu_));
00207     // }
00208     // else {
00209     //   ROS_DEBUG("no need to update texture size");
00210     // }
00211     if (next_menu_->action == jsk_rviz_plugins::OverlayMenu::ACTION_CLOSE) {
00212       // need to close...
00213       if (animation_state_ == CLOSED) {
00214         // do nothing, it should be ignored above if sentence
00215         ROS_WARN("request is CLOSE and state is CLOSED, it should be ignored before...");
00216       }
00217       else if (animation_state_ == OPENED) { // OPENED -> CLOSING
00218         animation_state_ = CLOSING;
00219         animation_t_ = animate_duration;
00220       }
00221       else if (animation_state_ == CLOSING) {
00222         animation_t_ -= wall_dt;
00223         if (animation_t_ > 0) { // CLOSING -> CLOSING
00224           openingAnimation();
00225         }
00226         else { // CLOSING -> CLOSED
00227           animation_t_ = 0;
00228           openingAnimation();
00229           animation_state_ = CLOSED;
00230         }
00231       }
00232       else if (animation_state_ == OPENING) { // if the status is OPENING, we open it anyway...??
00233         animation_t_ += wall_dt;
00234         if (animation_t_ < animate_duration) { // OPENING -> OPENING
00235           openingAnimation();
00236         }
00237         else {                  // OPENING -> OPENED
00238           redraw();
00239           animation_state_ = OPENED;
00240         }
00241       }
00242     }
00243     else {                      // OPEN request
00244       if (animation_state_ == CLOSED) { // CLOSED -> OPENING, do nothing just change the state
00245         animation_t_ = 0.0;
00246         animation_state_ = OPENING;
00247       }
00248       else if (animation_state_ == OPENING) {
00249         animation_t_ += wall_dt;
00250         ROS_DEBUG("animation_t: %f", animation_t_);
00251         if (animation_t_ < animate_duration) { // OPENING -> OPENING
00252           openingAnimation();
00253         }
00254         else {                  // OPENING -> OPENED
00255           redraw();
00256           animation_state_ = OPENED;
00257         }
00258       }
00259       else if (animation_state_ == OPENED) { // OPENED -> OPENED
00260         if (isNeedToRedraw()) {
00261           redraw();
00262         }
00263       }
00264       else if (animation_state_ == CLOSING) { // CLOSING, we close it anyway...
00265         animation_t_ -= wall_dt;
00266         if (animation_t_ > 0) {
00267           openingAnimation();
00268         }
00269         else {
00270           animation_t_ = 0;
00271           openingAnimation();
00272           animation_state_ = CLOSED;
00273         }
00274       }
00275     }
00276     //redraw();
00277     //current_menu_ = next_menu_;
00278   }
00279 
00280   bool OverlayMenuDisplay::isNeedToRedraw() {
00281     return true;
00282   }
00283 
00284   std::string OverlayMenuDisplay::getMenuString(
00285     const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg,
00286     size_t index)
00287   {
00288     if (index >= msg->menus.size()) {
00289       return "";
00290     }
00291     else {
00292       return msg->menus[index];
00293     }
00294   }
00295 
00296   void OverlayMenuDisplay::prepareOverlay()
00297   {
00298     if (!overlay_) {
00299       static int count = 0;
00300       rviz::UniformStringStream ss;
00301       ss << "OverlayMenuDisplayObject" << count++;
00302       overlay_.reset(new OverlayObject(ss.str()));
00303       overlay_->show();
00304     }
00305     if (!overlay_->isTextureReady() || isNeedToResize()) {
00306       overlay_->updateTextureSize(drawAreaWidth(next_menu_), drawAreaHeight(next_menu_));
00307     }
00308     else {
00309       ROS_DEBUG("no need to update texture size");
00310     }
00311   }
00312   
00313   void OverlayMenuDisplay::openingAnimation()
00314   {
00315     ROS_DEBUG("openningAnimation");
00316     prepareOverlay();
00317     int current_width = animation_t_ / animate_duration * overlay_->getTextureWidth();
00318     int current_height = animation_t_ / animate_duration * overlay_->getTextureHeight();
00319     {
00320       ScopedPixelBuffer buffer = overlay_->getBuffer();
00321       QColor bg_color(0, 0, 0, 255.0 / 2.0);
00322       QColor transparent(0, 0, 0, 0.0);
00323       QImage Hud = buffer.getQImage(*overlay_);
00324       for (int i = 0; i < overlay_->getTextureWidth(); i++) {
00325         for (int j = 0; j < overlay_->getTextureHeight(); j++) {
00326           if (i > (overlay_->getTextureWidth() - current_width) / 2.0 &&
00327               i < overlay_->getTextureWidth() - (overlay_->getTextureWidth() - current_width) / 2.0 &&
00328               j > (overlay_->getTextureHeight() - current_height) / 2.0 &&
00329               j < overlay_->getTextureHeight() - (overlay_->getTextureHeight() - current_height) / 2.0) {
00330             Hud.setPixel(i, j, bg_color.rgba());
00331           }
00332           else {
00333             Hud.setPixel(i, j, transparent.rgba());
00334           }
00335         }
00336       }
00337     }
00338     overlay_->setDimensions(overlay_->getTextureWidth(), overlay_->getTextureHeight());
00339     int window_width = context_->getViewManager()->getRenderPanel()->width();
00340     int window_height = context_->getViewManager()->getRenderPanel()->height();
00341     overlay_->setPosition((window_width - overlay_->getTextureWidth()) / 2.0,
00342                           (window_height - overlay_->getTextureHeight()) / 2.0);
00343     current_menu_ = next_menu_;
00344   }
00345   
00346   void OverlayMenuDisplay::redraw()
00347   {
00348     ROS_DEBUG("redraw");
00349     prepareOverlay();
00350     {
00351       ScopedPixelBuffer buffer = overlay_->getBuffer();
00352       QColor bg_color(0, 0, 0, 255.0 / 2.0);
00353       QColor fg_color(25, 255, 240, 255.0);
00354       QImage Hud = buffer.getQImage(*overlay_, bg_color);
00355       QPainter painter( &Hud );
00356       painter.setRenderHint(QPainter::Antialiasing, true);
00357       painter.setPen(QPen(fg_color, 1, Qt::SolidLine));
00358       painter.setFont(font());
00359       int line_height = fontMetrics().height();
00360       int w = drawAreaWidth(next_menu_);
00361       painter.drawText(menu_padding_x,  menu_padding_y,
00362                        w, line_height,
00363                        Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00364                        next_menu_->title.c_str());
00365       for (size_t i = 0; i < next_menu_->menus.size(); i++) {
00366         std::string menu = getMenuString(next_menu_, i);
00367         painter.drawText(menu_padding_x, line_height * ( 1 + i ) + menu_padding_y + menu_last_padding_y,
00368                          w, line_height,
00369                          Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00370                          menu.c_str());
00371       }
00372       if (next_menu_->current_index <= next_menu_->menus.size()) {
00373         // draw '>'
00374         painter.drawText(menu_padding_x - fontMetrics().width(">") * 2,
00375                          line_height * ( 1 + next_menu_->current_index ) + menu_padding_y + menu_last_padding_y,
00376                          w, line_height,
00377                          Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00378                          ">");
00379       }
00380       // draw line
00381       int texture_width = overlay_->getTextureWidth();
00382       int texture_height = overlay_->getTextureHeight();
00383       painter.drawLine(menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00384                        menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00385       painter.drawLine(texture_width - menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00386                        texture_width - menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00387       painter.drawLine(menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00388                        texture_width - menu_padding_x / 2, menu_last_padding_y / 2 + line_height);
00389       painter.drawLine(menu_padding_x / 2, texture_height - menu_last_padding_y / 2,
00390                        texture_width - menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00391       
00392       painter.end();
00393       current_menu_ = next_menu_;
00394     }
00395     overlay_->setDimensions(overlay_->getTextureWidth(), overlay_->getTextureHeight());
00396     int window_width = context_->getViewManager()->getRenderPanel()->width();
00397     int window_height = context_->getViewManager()->getRenderPanel()->height();
00398     overlay_->setPosition((window_width - overlay_->getTextureWidth()) / 2.0,
00399                           (window_height - overlay_->getTextureHeight()) / 2.0);
00400   }
00401   
00402   void OverlayMenuDisplay::updateTopic()
00403   {
00404     unsubscribe();
00405     subscribe();
00406   }
00407 }
00408 
00409 #include <pluginlib/class_list_macros.h>
00410 PLUGINLIB_EXPORT_CLASS( jsk_rviz_plugin::OverlayMenuDisplay, rviz::Display )


jsk_rviz_plugins
Author(s): Kei Okada , Yohei Kakiuchi , Shohei Fujii , Ryohei Ueda
autogenerated on Mon Oct 6 2014 01:18:44