horizontal_image_drawer.hpp
Go to the documentation of this file.
1 #ifndef RADIAL_MENU_RVIZ_HORIZONTAL_IMAGE_DRAWER_HPP
2 #define RADIAL_MENU_RVIZ_HORIZONTAL_IMAGE_DRAWER_HPP
3 
4 #include <algorithm>
5 #include <cstdint>
6 #include <vector>
7 
11 #include <ros/console.h>
12 #include <rviz/load_resource.h>
13 
14 #include <QBrush>
15 #include <QColor>
16 #include <QFontMetrics>
17 #include <QImage>
18 #include <QMargins>
19 #include <QPainter>
20 #include <QPen>
21 #include <QPoint>
22 #include <QRect>
23 #include <QRgb>
24 #include <QSize>
25 #include <QString>
26 
27 namespace radial_menu_rviz {
28 
30 public:
32  const HorizontalDrawingProperty &prop) {
33  setModel(model);
34  setProperty(prop);
35  }
36 
38 
39  void setModel(const radial_menu_model::ModelConstPtr &model) { model_ = model; }
40 
41  void setProperty(const HorizontalDrawingProperty &prop) { prop_ = prop; }
42 
43  QImage draw() const {
44  // plan element layouts on the image
45  QSize image_size;
46  std::vector< ElementType > element_types;
47  std::vector< QRect > bg_rects, fg_rects;
48  std::vector< radial_menu_model::ItemConstPtr > items;
49  imageLayout(&image_size, &element_types, &bg_rects, &fg_rects, &items);
50 
51  // draw elements on the image
52  QImage image(ImageOverlay::formattedImage(image_size, Qt::transparent));
53  QPainter painter(&image);
54  painter.setFont(prop_.font);
55  painter.setRenderHint(QPainter::TextAntialiasing);
56  painter.setRenderHint(QPainter::Antialiasing);
57  for (std::size_t i = 0; i < element_types.size(); ++i) {
58  switch (element_types[i]) {
59  case TitleElement:
60  drawBackground(&painter, prop_.title_bg_rgb, bg_rects[i]);
61  drawForeground(&painter, prop_.title_rgb, fg_rects[i], items[i]);
62  break;
63  case SelectedElement:
64  drawBackground(&painter, prop_.item_bg_rgb_selected, bg_rects[i]);
65  drawForeground(&painter, prop_.item_rgb_selected, fg_rects[i], items[i]);
66  break;
67  case PointedElement:
68  drawBackground(&painter, prop_.item_bg_rgb_pointed, bg_rects[i]);
69  drawForeground(&painter, prop_.item_rgb_pointed, fg_rects[i], items[i]);
70  break;
71  default:
72  ROS_ERROR_STREAM("HorizontalImageDrawer::draw(): unexpected element type ("
73  << static_cast< int >(element_types[i]) << "). Will not draw.");
74  break;
75  }
76  }
77 
78  return image;
79  }
80 
81 protected:
83 
84  // layout functions
85 
86  // calc image size and element layouts
87  void imageLayout(QSize *const image_size, std::vector< ElementType > *const element_types,
88  std::vector< QRect > *const bg_rects, std::vector< QRect > *const fg_rects,
89  std::vector< radial_menu_model::ItemConstPtr > *const items) const {
90  element_types->clear();
91  bg_rects->clear();
92  fg_rects->clear();
93  items->clear();
94 
95  //
96  // 1. enumerate drawable elements
97  //
98 
99  // evaluate the title element
100  {
101  const radial_menu_model::ItemConstPtr item(model_->root());
102  QRect bg_rect, fg_rect;
103  elementLayout(item, &bg_rect, &fg_rect);
104  if (bg_rect.isValid() && fg_rect.isValid()) {
105  element_types->push_back(TitleElement);
106  bg_rects->push_back(bg_rect);
107  fg_rects->push_back(fg_rect);
108  items->push_back(item);
109  }
110  }
111 
112  // evaluate the selected elements
113  for (const radial_menu_model::ItemConstPtr &item : model_->selected()) {
114  QRect bg_rect, fg_rect;
115  elementLayout(item, &bg_rect, &fg_rect);
116  if (bg_rect.isValid() && fg_rect.isValid()) {
117  element_types->push_back(SelectedElement);
118  bg_rects->push_back(bg_rect);
119  fg_rects->push_back(fg_rect);
120  items->push_back(item);
121  }
122  }
123 
124  // evaluate the pointed element
125  {
126  const radial_menu_model::ItemConstPtr item(model_->pointed());
127  if (item) {
128  QRect bg_rect, fg_rect;
129  elementLayout(item, &bg_rect, &fg_rect);
130  if (bg_rect.isValid() && fg_rect.isValid()) {
131  element_types->push_back(PointedElement);
132  bg_rects->push_back(bg_rect);
133  fg_rects->push_back(fg_rect);
134  items->push_back(item);
135  }
136  }
137  }
138 
139  //
140  // 2. layout elements
141  //
142 
143  // vertical layout of background elements
144  {
145  QRect united_rect;
146  for (const QRect &bg_rect : *bg_rects) {
147  united_rect |= bg_rect;
148  }
149  for (QRect &bg_rect : *bg_rects) {
150  bg_rect.setHeight(united_rect.height());
151  }
152  }
153 
154  // horizontal layout of background elements
155  for (std::size_t i = 1; i < bg_rects->size(); ++i) {
156  (*bg_rects)[i].moveLeft((*bg_rects)[i - 1].right() + prop_.line_width);
157  }
158 
159  // layout of foreground elements
160  for (std::size_t i = 0; i < bg_rects->size(); ++i) {
161  (*fg_rects)[i].moveCenter((*bg_rects)[i].center());
162  }
163 
164  //
165  // 3. image size
166  //
167 
168  image_size->setWidth(bg_rects->empty() ? 0 : bg_rects->back().right());
169  image_size->setHeight(bg_rects->empty() ? 0 : bg_rects->back().bottom());
170  }
171 
172  // calc bounding rectangles of element's background and foreground.
173  // the top left corner of the background rectangle will be aligined to (0, 0).
174  // if the text is not drawable, returns null rectangles.
175  void elementLayout(const radial_menu_model::ItemConstPtr &item, QRect *const bg_rect,
176  QRect *const fg_rect) const {
177  // set foreground rect
178  switch (item->displayType()) {
180  fg_rect->setHeight(prop_.fg_height);
181  fg_rect->setWidth(textWidth(prop_.font, QString::fromStdString(item->name())));
182  break;
184  fg_rect->setHeight(prop_.fg_height);
185  fg_rect->setWidth(textWidth(prop_.font, QString::fromStdString(item->altTxt())));
186  break;
188  fg_rect->setHeight(prop_.fg_height);
189  fg_rect->setWidth(prop_.fg_height);
190  break;
191  default:
192  ROS_ERROR_STREAM("HorizontalImageDrawer::elementLayout(): the item '"
193  << item->name() << "' has unexpected type ("
194  << static_cast< int >(item->displayType()) << ")");
195  fg_rect->setHeight(1);
196  fg_rect->setWidth(1);
197  break;
198  }
199 
200  // set background rect
201  *bg_rect = *fg_rect + uniformMargins(prop_.bg_padding);
202 
203  // align rects
204  bg_rect->translate(-bg_rect->topLeft());
205  fg_rect->moveCenter(bg_rect->center());
206  }
207 
208  static int textWidth(const QFont &font, const QString &text) {
209  return QFontMetrics(font).boundingRect(QRect(), Qt::AlignCenter, text).width();
210  }
211 
212  static QMargins uniformMargins(const int margin) {
213  return QMargins(margin, margin, margin, margin);
214  }
215 
216  // drawing functions
217 
218  void drawBackground(QPainter *const painter, const QRgb &rgb, const QRect &rect) const {
219  const QColor color(makeColor(rgb, prop_.bg_alpha));
220  painter->setPen(color);
221  painter->setBrush(color);
222  painter->drawRect(rect);
223  }
224 
225  void drawForeground(QPainter *const painter, const QRgb &rgb, const QRect &rect,
226  const radial_menu_model::ItemConstPtr &item) const {
227  painter->setPen(makeColor(rgb, prop_.fg_alpha));
228  switch (item->displayType()) {
230  painter->drawText(rect, Qt::AlignCenter, QString::fromStdString(item->name()));
231  break;
233  painter->drawText(rect, Qt::AlignCenter, QString::fromStdString(item->altTxt()));
234  break;
236  painter->drawPixmap(
237  rect, rviz::loadPixmap(QString::fromStdString(item->imgURL()), /* fill_cache = */ true));
238  break;
239  default:
240  ROS_ERROR_STREAM("HorizontalImageDrawer::drawForeground(): the item '"
241  << item->name() << "' has unexpected type ("
242  << static_cast< int >(item->displayType()) << ")");
243  break;
244  }
245  }
246 
247  static QColor makeColor(const QRgb &rgb, const int alpha) {
248  QColor color;
249  color.setRgb(rgb);
250  color.setAlpha(alpha);
251  return color;
252  }
253 
254 protected:
257 };
258 } // namespace radial_menu_rviz
259 
260 #endif
std::shared_ptr< const Item > ItemConstPtr
void setModel(const radial_menu_model::ModelConstPtr &model)
void drawForeground(QPainter *const painter, const QRgb &rgb, const QRect &rect, const radial_menu_model::ItemConstPtr &item) const
void imageLayout(QSize *const image_size, std::vector< ElementType > *const element_types, std::vector< QRect > *const bg_rects, std::vector< QRect > *const fg_rects, std::vector< radial_menu_model::ItemConstPtr > *const items) const
std::shared_ptr< const Model > ModelConstPtr
static QMargins uniformMargins(const int margin)
static int textWidth(const QFont &font, const QString &text)
void setProperty(const HorizontalDrawingProperty &prop)
HorizontalImageDrawer(const radial_menu_model::ModelConstPtr &model, const HorizontalDrawingProperty &prop)
static QImage formattedImage(const QSize &size, const QColor &color)
void elementLayout(const radial_menu_model::ItemConstPtr &item, QRect *const bg_rect, QRect *const fg_rect) const
#define ROS_ERROR_STREAM(args)
QPixmap loadPixmap(QString url, bool fill_cache)
void drawBackground(QPainter *const painter, const QRgb &rgb, const QRect &rect) const
static QColor makeColor(const QRgb &rgb, const int alpha)


radial_menu_rviz
Author(s):
autogenerated on Mon Feb 28 2022 23:22:04