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_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_) {
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 {
00137
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
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211 if (next_menu_->action == jsk_rviz_plugins::OverlayMenu::ACTION_CLOSE) {
00212
00213 if (animation_state_ == CLOSED) {
00214
00215 ROS_WARN("request is CLOSE and state is CLOSED, it should be ignored before...");
00216 }
00217 else if (animation_state_ == OPENED) {
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) {
00224 openingAnimation();
00225 }
00226 else {
00227 animation_t_ = 0;
00228 openingAnimation();
00229 animation_state_ = CLOSED;
00230 }
00231 }
00232 else if (animation_state_ == OPENING) {
00233 animation_t_ += wall_dt;
00234 if (animation_t_ < animate_duration) {
00235 openingAnimation();
00236 }
00237 else {
00238 redraw();
00239 animation_state_ = OPENED;
00240 }
00241 }
00242 }
00243 else {
00244 if (animation_state_ == CLOSED) {
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) {
00252 openingAnimation();
00253 }
00254 else {
00255 redraw();
00256 animation_state_ = OPENED;
00257 }
00258 }
00259 else if (animation_state_ == OPENED) {
00260 if (isNeedToRedraw()) {
00261 redraw();
00262 }
00263 }
00264 else if (animation_state_ == CLOSING) {
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
00277
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
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
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 )