00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
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_plugins
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_) {
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
00122
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
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 if (current_menu_->title != next_menu_->title) {
00137 return true;
00138 }
00139 else {
00140
00141 for (size_t i = 0; i < current_menu_->menus.size(); i++) {
00142 if (current_menu_->menus[i] != next_menu_->menus[i]) {
00143 ROS_DEBUG("need to resize because the content of menu is different");
00144 return true;
00145 }
00146 }
00147 ROS_DEBUG("no need to resize because the content of menu is same");
00148 return false;
00149 }
00150 }
00151 }
00152
00153 QFont OverlayMenuDisplay::font()
00154 {
00155 QFont font;
00156 font.setPointSize(20);
00157 return font;
00158 }
00159
00160 QFontMetrics OverlayMenuDisplay::fontMetrics()
00161 {
00162 QFontMetrics fm(font());
00163 return fm;
00164 }
00165
00166 int OverlayMenuDisplay::drawAreaWidth(
00167 const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg)
00168 {
00169 QFontMetrics fm = fontMetrics();
00170 int max_width = 0;
00171 for (size_t i = 0; i < msg->menus.size(); i++) {
00172 int w = fm.width(getMenuString(msg, i).c_str());
00173 if (max_width < w) {
00174 max_width = w;
00175 }
00176 }
00177 int w = fm.width(msg->title.c_str());
00178
00179 if (max_width < w) {
00180 max_width = w;
00181 }
00182 return max_width + menu_padding_x * 2;
00183 }
00184
00185 int OverlayMenuDisplay::drawAreaHeight(
00186 const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg)
00187 {
00188 QFontMetrics fm = fontMetrics();
00189 return fm.height() * (msg->menus.size() + 1)
00190 + menu_padding_y * (msg->menus.size() + 1 - 1)
00191 + menu_last_padding_y * 2;
00192 }
00193
00194 void OverlayMenuDisplay::update(float wall_dt, float ros_dt)
00195 {
00196 if (!next_menu_) {
00197 ROS_DEBUG("next_menu_ is null, no need to update");
00198 return;
00199 }
00200 if (next_menu_->action == jsk_rviz_plugins::OverlayMenu::ACTION_CLOSE &&
00201 animation_state_ == CLOSED) {
00202 ROS_DEBUG("request is close and state is closed, we ignore it completely");
00203 return;
00204 }
00205
00206 if (next_menu_->action == jsk_rviz_plugins::OverlayMenu::ACTION_CLOSE) {
00207
00208 if (animation_state_ == CLOSED) {
00209
00210 ROS_WARN("request is CLOSE and state is CLOSED, it should be ignored before...");
00211 }
00212 else if (animation_state_ == OPENED) {
00213 animation_state_ = CLOSING;
00214 animation_t_ = animate_duration;
00215 }
00216 else if (animation_state_ == CLOSING) {
00217 animation_t_ -= wall_dt;
00218 if (animation_t_ > 0) {
00219 openingAnimation();
00220 }
00221 else {
00222 animation_t_ = 0;
00223 openingAnimation();
00224 animation_state_ = CLOSED;
00225 }
00226 }
00227 else if (animation_state_ == OPENING) {
00228 animation_t_ += wall_dt;
00229 if (animation_t_ < animate_duration) {
00230 openingAnimation();
00231 }
00232 else {
00233 redraw();
00234 animation_state_ = OPENED;
00235 }
00236 }
00237 }
00238 else {
00239 if (animation_state_ == CLOSED) {
00240 animation_t_ = 0.0;
00241 animation_state_ = OPENING;
00242 }
00243 else if (animation_state_ == OPENING) {
00244 animation_t_ += wall_dt;
00245 ROS_DEBUG("animation_t: %f", animation_t_);
00246 if (animation_t_ < animate_duration) {
00247 openingAnimation();
00248 }
00249 else {
00250 redraw();
00251 animation_state_ = OPENED;
00252 }
00253 }
00254 else if (animation_state_ == OPENED) {
00255 if (isNeedToRedraw()) {
00256 redraw();
00257 }
00258 }
00259 else if (animation_state_ == CLOSING) {
00260 animation_t_ -= wall_dt;
00261 if (animation_t_ > 0) {
00262 openingAnimation();
00263 }
00264 else {
00265 animation_t_ = 0;
00266 openingAnimation();
00267 animation_state_ = CLOSED;
00268 }
00269 }
00270 }
00271
00272
00273 }
00274
00275 bool OverlayMenuDisplay::isNeedToRedraw() {
00276 return true;
00277 }
00278
00279 std::string OverlayMenuDisplay::getMenuString(
00280 const jsk_rviz_plugins::OverlayMenu::ConstPtr& msg,
00281 size_t index)
00282 {
00283 if (index >= msg->menus.size()) {
00284 return "";
00285 }
00286 else {
00287 return msg->menus[index];
00288 }
00289 }
00290
00291 void OverlayMenuDisplay::prepareOverlay()
00292 {
00293 if (!overlay_) {
00294 static int count = 0;
00295 rviz::UniformStringStream ss;
00296 ss << "OverlayMenuDisplayObject" << count++;
00297 overlay_.reset(new OverlayObject(ss.str()));
00298 overlay_->show();
00299 }
00300 if (!overlay_->isTextureReady() || isNeedToResize()) {
00301 overlay_->updateTextureSize(drawAreaWidth(next_menu_), drawAreaHeight(next_menu_));
00302 }
00303 else {
00304 ROS_DEBUG("no need to update texture size");
00305 }
00306 }
00307
00308 void OverlayMenuDisplay::openingAnimation()
00309 {
00310 ROS_DEBUG("openningAnimation");
00311 prepareOverlay();
00312 int current_width = animation_t_ / animate_duration * overlay_->getTextureWidth();
00313 int current_height = animation_t_ / animate_duration * overlay_->getTextureHeight();
00314 {
00315 ScopedPixelBuffer buffer = overlay_->getBuffer();
00316 QColor bg_color(0, 0, 0, 255.0 / 2.0);
00317 QColor transparent(0, 0, 0, 0.0);
00318 QImage Hud = buffer.getQImage(*overlay_);
00319 for (int i = 0; i < overlay_->getTextureWidth(); i++) {
00320 for (int j = 0; j < overlay_->getTextureHeight(); j++) {
00321 if (i > (overlay_->getTextureWidth() - current_width) / 2.0 &&
00322 i < overlay_->getTextureWidth() - (overlay_->getTextureWidth() - current_width) / 2.0 &&
00323 j > (overlay_->getTextureHeight() - current_height) / 2.0 &&
00324 j < overlay_->getTextureHeight() - (overlay_->getTextureHeight() - current_height) / 2.0) {
00325 Hud.setPixel(i, j, bg_color.rgba());
00326 }
00327 else {
00328 Hud.setPixel(i, j, transparent.rgba());
00329 }
00330 }
00331 }
00332 }
00333 overlay_->setDimensions(overlay_->getTextureWidth(), overlay_->getTextureHeight());
00334 int window_width = context_->getViewManager()->getRenderPanel()->width();
00335 int window_height = context_->getViewManager()->getRenderPanel()->height();
00336 double window_left = (window_width - (int)overlay_->getTextureWidth()) / 2.0;
00337 double window_top = (window_height - (int)overlay_->getTextureHeight()) / 2.0;
00338 overlay_->setPosition(window_left, window_top);
00339
00340 current_menu_ = next_menu_;
00341 }
00342
00343 void OverlayMenuDisplay::redraw()
00344 {
00345 ROS_DEBUG("redraw");
00346 prepareOverlay();
00347 {
00348 ScopedPixelBuffer buffer = overlay_->getBuffer();
00349 QColor bg_color(0, 0, 0, 255.0 / 2.0);
00350 QColor fg_color(25, 255, 240, 255.0);
00351 QImage Hud = buffer.getQImage(*overlay_, bg_color);
00352 QPainter painter( &Hud );
00353 painter.setRenderHint(QPainter::Antialiasing, true);
00354 painter.setPen(QPen(fg_color, 1, Qt::SolidLine));
00355 painter.setFont(font());
00356 int line_height = fontMetrics().height();
00357 int w = drawAreaWidth(next_menu_);
00358 painter.drawText(menu_padding_x, menu_padding_y,
00359 w, line_height,
00360 Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00361 next_menu_->title.c_str());
00362 for (size_t i = 0; i < next_menu_->menus.size(); i++) {
00363 std::string menu = getMenuString(next_menu_, i);
00364 painter.drawText(menu_padding_x, line_height * ( 1 + i ) + menu_padding_y + menu_last_padding_y,
00365 w, line_height,
00366 Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00367 menu.c_str());
00368 }
00369 if (next_menu_->current_index <= next_menu_->menus.size()) {
00370
00371 painter.drawText(menu_padding_x - fontMetrics().width(">") * 2,
00372 line_height * ( 1 + next_menu_->current_index ) + menu_padding_y + menu_last_padding_y,
00373 w, line_height,
00374 Qt::TextWordWrap | Qt::AlignLeft | Qt::AlignTop,
00375 ">");
00376 }
00377
00378 int texture_width = overlay_->getTextureWidth();
00379 int texture_height = overlay_->getTextureHeight();
00380 painter.drawLine(menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00381 menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00382 painter.drawLine(texture_width - menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00383 texture_width - menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00384 painter.drawLine(menu_padding_x / 2, menu_last_padding_y / 2 + line_height,
00385 texture_width - menu_padding_x / 2, menu_last_padding_y / 2 + line_height);
00386 painter.drawLine(menu_padding_x / 2, texture_height - menu_last_padding_y / 2,
00387 texture_width - menu_padding_x / 2, texture_height - menu_last_padding_y / 2);
00388
00389 painter.end();
00390 current_menu_ = next_menu_;
00391 }
00392 overlay_->setDimensions(overlay_->getTextureWidth(), overlay_->getTextureHeight());
00393 int window_width = context_->getViewManager()->getRenderPanel()->width();
00394 int window_height = context_->getViewManager()->getRenderPanel()->height();
00395 double window_left = (window_width - (int)overlay_->getTextureWidth()) / 2.0;
00396 double window_top = (window_height - (int)overlay_->getTextureHeight()) / 2.0;
00397 overlay_->setPosition(window_left, window_top);
00398 }
00399
00400 void OverlayMenuDisplay::updateTopic()
00401 {
00402 unsubscribe();
00403 subscribe();
00404 }
00405 }
00406
00407 #include <pluginlib/class_list_macros.h>
00408 PLUGINLIB_EXPORT_CLASS( jsk_rviz_plugins::OverlayMenuDisplay, rviz::Display )