radial_image_drawer.hpp
Go to the documentation of this file.
1 #ifndef RADIAL_MENU_RVIZ_RADIAL_IMAGE_DRAWER_HPP
2 #define RADIAL_MENU_RVIZ_RADIAL_IMAGE_DRAWER_HPP
3 
4 #include <algorithm>
5 #include <cmath>
6 #include <vector>
7 
11 #include <ros/console.h>
12 #include <rviz/load_resource.h>
13 
14 #include <QBrush>
15 #include <QColor>
16 #include <QImage>
17 #include <QPaintDevice>
18 #include <QPainter>
19 #include <QPen>
20 #include <QPoint>
21 #include <QRect>
22 #include <QRgb>
23 #include <QSize>
24 #include <QString>
25 
26 namespace radial_menu_rviz {
27 
29 public:
31  const RadialDrawingProperty &prop) {
32  setModel(model);
33  setProperty(prop);
34  }
35 
36  virtual ~RadialImageDrawer() {}
37 
38  void setModel(const radial_menu_model::ModelConstPtr &model) { model_ = model; }
39 
40  void setProperty(const RadialDrawingProperty &prop) { prop_ = prop; }
41 
42  QImage draw() const {
43  // if the menu is disabled, draw nothing
44  if (!model_->isEnabled()) {
45  return QImage();
46  }
47 
48  // draw menu elements
49  QImage image(
50  ImageOverlay::formattedImage(imageSize(model_->currentLevel()->depth()), Qt::transparent));
51  drawBackgrounds(&image);
52  drawForegrounds(&image);
53  return image;
54  }
55 
56 protected:
57  // drawing functions
58 
59  void drawBackgrounds(QImage *const image) const {
60  // prepare alpha channel to separately draw
61  QImage alpha_image(image->size(), QImage::Format_Grayscale8);
62  alpha_image.fill(QColor(0, 0, 0)); // defaultly transparent
63 
64  // painters
65  QPainter rgb_painter(image), alpha_painter(&alpha_image);
66  rgb_painter.setRenderHint(QPainter::Antialiasing);
67  alpha_painter.setRenderHint(QPainter::Antialiasing);
68 
69  // draw item areas from outer to inner, and then the title area
70  for (radial_menu_model::ItemConstPtr level = model_->currentLevel(); level != model_->root();
71  level = level->parentLevel()) {
72  drawItemBackgrounds(&rgb_painter, &alpha_painter, level);
73  }
74  drawTitleBackground(&rgb_painter, &alpha_painter);
75 
76  // merge the alpha channel to the given image
77  rgb_painter.end();
78  alpha_painter.end();
79  image->setAlphaChannel(alpha_image);
80  }
81 
82  void drawItemBackgrounds(QPainter *const rgb_painter, QPainter *const alpha_painter,
83  const radial_menu_model::ItemConstPtr &level) const {
84  // common properties
85  const QPoint image_center(deviceCenter(*rgb_painter->device()));
86  const int n_sibilings(level->numSibilings()), depth(level->depth());
87 
88  // draw item areas by pies
89  if (n_sibilings > 0) {
90  // set tools for alpha painter
91  const QColor bg_alpha(prop_.bg_alpha, prop_.bg_alpha, prop_.bg_alpha);
92  alpha_painter->setPen(bg_alpha);
93  alpha_painter->setBrush(bg_alpha);
94  // pie's property
95  QRect rect;
96  rect.setSize(imageSize(depth));
97  rect.moveCenter(image_center);
98  const int span_angle(pieSpanAngle(n_sibilings));
99  // draw pies
100  for (int sid = 0; sid < n_sibilings; ++sid) {
101  const radial_menu_model::ItemConstPtr item(level->sibiling(sid));
102  if (!item) {
103  continue;
104  }
105  // set tools for rgb painter according to item type
106  const bool is_selected(model_->isSelected(item)), is_pointed(model_->isPointed(item));
107  if (is_selected && is_pointed) {
109  rgb_painter->setPen(QPen(rgb));
110  rgb_painter->setBrush(QBrush(rgb));
111  } else if (is_selected && !is_pointed) {
112  rgb_painter->setPen(QPen(prop_.item_bg_rgb_selected));
113  rgb_painter->setBrush(QBrush(prop_.item_bg_rgb_selected));
114  } else if (!is_selected && is_pointed) {
116  rgb_painter->setPen(QPen(rgb));
117  rgb_painter->setBrush(QBrush(rgb));
118  } else { // !is_selected && !is_pointed
119  rgb_painter->setPen(QPen(prop_.item_bg_rgb_default));
120  rgb_painter->setBrush(QBrush(prop_.item_bg_rgb_default));
121  }
122  // draw a pie
123  const int center_angle(pieCenterAngle(sid, n_sibilings));
124  const int start_angle(center_angle - span_angle / 2);
125  rgb_painter->drawPie(rect, start_angle, span_angle);
126  alpha_painter->drawPie(rect, start_angle, span_angle);
127  }
128  }
129 
130  // draw transparent lines between sibiling items
131  if (n_sibilings > 0 && prop_.line_width > 0) {
132  alpha_painter->setPen(QPen(QColor(0, 0, 0), prop_.line_width));
133  for (int sid = 0; sid < n_sibilings; ++sid) {
134  alpha_painter->drawLine(image_center,
135  image_center + relativeItemLineEnd(sid, n_sibilings, depth));
136  }
137  }
138 
139  // fill inner area with transparent circle
140  {
141  alpha_painter->setPen(QPen(QColor(0, 0, 0), prop_.line_width));
142  alpha_painter->setBrush(QColor(0, 0, 0));
143  QRect rect;
144  rect.setSize(imageSize(depth - 1) + QSize(prop_.line_width, prop_.line_width));
145  rect.moveCenter(image_center);
146  alpha_painter->drawEllipse(rect);
147  }
148  }
149 
150  void drawTitleBackground(QPainter *const rgb_painter, QPainter *const alpha_painter) const {
151  // draw the title area by a filled circle
152  if (prop_.title_area_radius > 0) {
153  // circle properties
154  const QPoint image_center(deviceCenter(*rgb_painter->device()));
155  const QPoint relative_corner(prop_.title_area_radius, prop_.title_area_radius);
156  const QRect rect(image_center - relative_corner, image_center + relative_corner);
157  if (prop_.draw_title_area) {
158  //
159  rgb_painter->setPen(QPen(prop_.title_bg_rgb));
160  rgb_painter->setBrush(QBrush(prop_.title_bg_rgb));
161  rgb_painter->drawEllipse(rect);
162  //
163  const QColor bg_alpha(prop_.bg_alpha, prop_.bg_alpha, prop_.bg_alpha);
164  alpha_painter->setPen(bg_alpha);
165  alpha_painter->setBrush(bg_alpha);
166  alpha_painter->drawEllipse(rect);
167  } else {
168  alpha_painter->setPen(QColor(0, 0, 0));
169  alpha_painter->setBrush(QColor(0, 0, 0));
170  alpha_painter->drawEllipse(rect);
171  }
172  }
173  }
174 
175  void drawForegrounds(QImage *const image) const {
176  QPainter painter(image);
177  painter.setFont(prop_.font);
178  painter.setRenderHint(QPainter::TextAntialiasing);
179  painter.setRenderHint(QPainter::Antialiasing);
180 
181  for (radial_menu_model::ItemConstPtr level = model_->currentLevel(); level != model_->root();
182  level = level->parentLevel()) {
183  drawItemForegrounds(&painter, level);
184  }
185  drawTitleForeground(&painter);
186  }
187 
188  void drawItemForegrounds(QPainter *const painter,
189  const radial_menu_model::ItemConstPtr &level) const {
190  const QPoint image_center(deviceCenter(*painter->device()));
191  const int n_sibilings(level->numSibilings()), depth(level->depth());
192 
193  // draw item texts
194  for (int sid = 0; sid < n_sibilings; ++sid) {
195  const radial_menu_model::ItemConstPtr item(level->sibiling(sid));
196  if (!item) {
197  continue;
198  }
199  // set the item bounding rect
200  QRect rect;
201  rect.setWidth(prop_.item_area_width);
202  rect.setHeight(prop_.item_area_width);
203  rect.moveCenter(relativeItemCenter(sid, n_sibilings, depth));
204  rect.translate(image_center);
205  // draw the item with color according to item type
206  const bool is_selected(model_->isSelected(item)), is_pointed(model_->isPointed(item));
207  if (is_selected && is_pointed) {
209  rect, item);
210  } else if (is_selected && !is_pointed) {
211  drawItemForeground(painter, prop_.item_rgb_selected, rect, item);
212  } else if (!is_selected && is_pointed) {
214  rect, item);
215  } else { // !is_selected && !is_pointed
216  drawItemForeground(painter, prop_.item_rgb_default, rect, item);
217  }
218  }
219  }
220 
221  void drawTitleForeground(QPainter *const painter) const {
222  if (prop_.draw_title_area) {
223  QRect rect;
224  rect.setWidth(2 * prop_.title_area_radius);
225  rect.setHeight(2 * prop_.title_area_radius);
226  rect.moveCenter(deviceCenter(*painter->device()));
227  drawItemForeground(painter, prop_.title_rgb, rect, model_->root());
228  }
229  }
230 
231  void drawItemForeground(QPainter *const painter, const QRgb &rgb, const QRect &rect,
232  const radial_menu_model::ItemConstPtr &item) const {
233  painter->setPen(makeColor(rgb, prop_.fg_alpha));
234  switch (item->displayType()) {
236  painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap,
237  QString::fromStdString(item->name()));
238  return;
240  painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap,
241  QString::fromStdString(item->altTxt()));
242  return;
244  painter->drawPixmap(
245  rect, rviz::loadPixmap(QString::fromStdString(item->imgURL()), /* fill_cache = */ true));
246  return;
247  default:
248  ROS_ERROR_STREAM("RadialImageDrawer::drawItemForeground(): the item '"
249  << item->name() << "' has unexpected type ("
250  << static_cast< int >(item->displayType()) << ")");
251  return;
252  }
253  }
254 
255  // helper functions
256 
257  // if the depth is 0, returns the size of title area
258  QSize imageSize(const int depth) const {
259  const int len(2 *
261  return QSize(len, len);
262  }
263 
264  // angle for QPainter (0 at 3 o'clock, counterclockwise positive, in 1/16 degrees)
265  int pieCenterAngle(const int sid, const int n_sibilings) const {
266  return (n_sibilings == 0) ? 0 : (360 * 16 * sid / n_sibilings + 90 * 16);
267  }
268 
269  // angle for QPainter (in 1/16 degrees)
270  int pieSpanAngle(const int n_sibilings) const {
271  return (n_sibilings == 0) ? 0 : (360 * 16 / n_sibilings);
272  }
273 
274  // calc the end position of the line between i-th and (i+1)-th item areas,
275  // relative to the menu center
276  QPoint relativeItemLineEnd(const int sid, const int n_sibilings, const int depth) const {
277  // 0 at twelve o'clock position, counterclockwise positive
278  const double th((n_sibilings == 0) ? 0. : (2. * M_PI * (0.5 + sid) / n_sibilings));
279  // upward & leftward positive
280  const double radius(prop_.title_area_radius +
281  (prop_.line_width + prop_.item_area_width) * depth);
282  const double u(radius * std::cos(th)), v(radius * std::sin(th));
283  // rightward & downward positive
284  return QPoint(-static_cast< int >(v), -static_cast< int >(u));
285  }
286 
287  // calc the center position of item text, relative to the menu center
288  QPoint relativeItemCenter(const int sid, const int n_sibilings, const int depth) const {
289  // 0 at twelve o'clock position, counterclockwise positive
290  const double th((n_sibilings == 0) ? 0. : (2. * M_PI * sid / n_sibilings));
291  // upward & leftward positive
292  const double radius(prop_.title_area_radius + prop_.line_width * depth +
293  prop_.item_area_width * (depth - 0.5));
294  const double u(radius * std::cos(th)), v(radius * std::sin(th));
295  // rightward & downward positive
296  return QPoint(-static_cast< int >(v), -static_cast< int >(u));
297  }
298 
299  static QPoint deviceCenter(const QPaintDevice &device) {
300  return QPoint(device.width() / 2, device.height() / 2);
301  }
302 
303  static QRgb averagedRgb(const QRgb &rgb1, const QRgb &rgb2) {
304  const QColor color1(rgb1), color2(rgb2);
305  return QColor((color1.red() + color2.red()) / 2, (color1.green() + color2.green()) / 2,
306  (color1.blue() + color2.blue()) / 2)
307  .rgb();
308  }
309 
310  static QColor makeColor(const QRgb &rgb, const int alpha) {
311  QColor color;
312  color.setRgb(rgb);
313  color.setAlpha(alpha);
314  return color;
315  }
316 
317 protected:
320 };
321 } // namespace radial_menu_rviz
322 
323 #endif
std::shared_ptr< const Item > ItemConstPtr
void drawItemForeground(QPainter *const painter, const QRgb &rgb, const QRect &rect, const radial_menu_model::ItemConstPtr &item) const
void drawForegrounds(QImage *const image) const
void drawItemBackgrounds(QPainter *const rgb_painter, QPainter *const alpha_painter, const radial_menu_model::ItemConstPtr &level) const
void drawBackgrounds(QImage *const image) const
QPoint relativeItemCenter(const int sid, const int n_sibilings, const int depth) const
QPoint relativeItemLineEnd(const int sid, const int n_sibilings, const int depth) const
static QPoint deviceCenter(const QPaintDevice &device)
void setProperty(const RadialDrawingProperty &prop)
void drawItemForegrounds(QPainter *const painter, const radial_menu_model::ItemConstPtr &level) const
QSize imageSize(const int depth) const
std::shared_ptr< const Model > ModelConstPtr
static QRgb averagedRgb(const QRgb &rgb1, const QRgb &rgb2)
RadialImageDrawer(const radial_menu_model::ModelConstPtr &model, const RadialDrawingProperty &prop)
void drawTitleBackground(QPainter *const rgb_painter, QPainter *const alpha_painter) const
void drawTitleForeground(QPainter *const painter) const
int pieCenterAngle(const int sid, const int n_sibilings) const
void setModel(const radial_menu_model::ModelConstPtr &model)
static QImage formattedImage(const QSize &size, const QColor &color)
radial_menu_model::ModelConstPtr model_
#define ROS_ERROR_STREAM(args)
int pieSpanAngle(const int n_sibilings) const
QPixmap loadPixmap(QString url, bool fill_cache)
static QColor makeColor(const QRgb &rgb, const int alpha)


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