color_wheel.cpp
Go to the documentation of this file.
1 
22 #include "color_wheel.hpp"
23 
24 #include <cmath>
25 #include <QMouseEvent>
26 #include <QPainter>
27 #include <QPainterPath>
28 #include <QLineF>
29 #include <QDragEnterEvent>
30 #include <QMimeData>
31 #include "color_utils.hpp"
32 
33 namespace color_widgets {
34 
36 {
40 };
41 
43 static ColorWheel::DisplayFlags default_flags = hard_default_flags;
44 static const double selector_radius = 6;
45 
47 {
48 private:
49  ColorWheel * const w;
50 
51 public:
52  qreal hue, sat, val;
53  unsigned int wheel_width;
55  QPixmap hue_ring;
57  DisplayFlags display_flags;
58  QColor (*color_from)(qreal,qreal,qreal,qreal);
59  QColor (*rainbow_from_hue)(qreal);
60  int max_size = 128;
61 
63  : w(widget), hue(0), sat(0), val(0),
64  wheel_width(20), mouse_status(Nothing),
65  display_flags(FLAGS_DEFAULT),
66  color_from(&QColor::fromHsvF), rainbow_from_hue(&detail::rainbow_hsv)
67  { }
68 
70  qreal outer_radius() const
71  {
72  return qMin(w->geometry().width(), w->geometry().height())/2;
73  }
74 
76  qreal inner_radius() const
77  {
78  return outer_radius()-wheel_width;
79  }
80 
82  qreal square_size() const
83  {
84  return inner_radius()*qSqrt(2);
85  }
86 
88  qreal triangle_height() const
89  {
90  return inner_radius()*3/2;
91  }
92 
94  qreal triangle_side() const
95  {
96  return inner_radius()*qSqrt(3);
97  }
98 
100  QLineF line_to_point(const QPoint &p) const
101  {
102  return QLineF (w->geometry().width()/2, w->geometry().height()/2, p.x(), p.y());
103  }
104 
106  {
107  int width = qMin<int>(square_size(), max_size);
108  QSize size(width, width);
109  inner_selector = QImage(size, QImage::Format_RGB32);
110 
111  for ( int y = 0; y < width; ++y )
112  {
113  for ( int x = 0; x < width; ++x )
114  {
115  inner_selector.setPixel( x, y,
116  color_from(hue,double(x)/width,double(y)/width,1).rgb());
117  }
118  }
119  }
120 
126  {
127  QSizeF size = selector_size();
128  if ( size.height() > max_size )
129  size *= max_size / size.height();
130 
131  qreal ycenter = size.height()/2;
132  inner_selector = QImage(size.toSize(), QImage::Format_RGB32);
133 
134  for (int x = 0; x < inner_selector.width(); x++ )
135  {
136  qreal pval = x / size.height();
137  qreal slice_h = size.height() * pval;
138  for (int y = 0; y < inner_selector.height(); y++ )
139  {
140  qreal ymin = ycenter-slice_h/2;
141  qreal psat = qBound(0.0,(y-ymin)/slice_h,1.0);
142 
143  inner_selector.setPixel(x,y,color_from(hue,psat,pval,1).rgb());
144  }
145  }
146  }
147 
150  {
151  if ( display_flags & ColorWheel::SHAPE_TRIANGLE )
152  render_triangle();
153  else
154  render_square();
155  }
156 
159  {
160  if ( display_flags & SHAPE_TRIANGLE )
161  return QPointF(-inner_radius(),-triangle_side()/2);
162  return QPointF(-square_size()/2,-square_size()/2);
163  }
164 
168  QSizeF selector_size()
169  {
170  if ( display_flags & SHAPE_TRIANGLE )
171  return QSizeF(triangle_height(), triangle_side());
172  return QSizeF(square_size(), square_size());
173  }
174 
175 
178  {
179  if ( display_flags & SHAPE_TRIANGLE )
180  {
181  if ( display_flags & ANGLE_ROTATING )
182  return -hue*360-60;
183  return -150;
184  }
185  else
186  {
187  if ( display_flags & ANGLE_ROTATING )
188  return -hue*360-45;
189  else
190  return 180;
191  }
192  }
193 
195  void render_ring()
196  {
197  hue_ring = QPixmap(outer_radius()*2,outer_radius()*2);
198  hue_ring.fill(Qt::transparent);
199  QPainter painter(&hue_ring);
200  painter.setRenderHint(QPainter::Antialiasing);
201  painter.setCompositionMode(QPainter::CompositionMode_Source);
202 
203 
204  const int hue_stops = 24;
205  QConicalGradient gradient_hue(0, 0, 0);
206  if ( gradient_hue.stops().size() < hue_stops )
207  {
208  for ( double a = 0; a < 1.0; a+=1.0/(hue_stops-1) )
209  {
210  gradient_hue.setColorAt(a,rainbow_from_hue(a));
211  }
212  gradient_hue.setColorAt(1,rainbow_from_hue(0));
213  }
214 
215  painter.translate(outer_radius(),outer_radius());
216 
217  painter.setPen(Qt::NoPen);
218  painter.setBrush(QBrush(gradient_hue));
219  painter.drawEllipse(QPointF(0,0),outer_radius(),outer_radius());
220 
221  painter.setBrush(Qt::transparent);//palette().background());
222  painter.drawEllipse(QPointF(0,0),inner_radius(),inner_radius());
223  }
224 
225  void set_color(const QColor& c)
226  {
227  if ( display_flags & ColorWheel::COLOR_HSV )
228  {
229  hue = qMax(0.0, c.hsvHueF());
230  sat = c.hsvSaturationF();
231  val = c.valueF();
232  }
233  else if ( display_flags & ColorWheel::COLOR_HSL )
234  {
235  hue = qMax(0.0, c.hueF());
237  val = detail::color_lightnessF(c);
238  }
239  else if ( display_flags & ColorWheel::COLOR_LCH )
240  {
241  hue = qMax(0.0, c.hsvHueF());
242  sat = detail::color_chromaF(c);
243  val = detail::color_lumaF(c);
244  }
245  }
246 };
247 
248 ColorWheel::ColorWheel(QWidget *parent) :
249  QWidget(parent), p(new Private(this))
250 {
252  setAcceptDrops(true);
253 }
254 
256 {
257  delete p;
258 }
259 
260 QColor ColorWheel::color() const
261 {
262  return p->color_from(p->hue, p->sat, p->val, 1);
263 }
264 
265 QSize ColorWheel::sizeHint() const
266 {
267  return QSize(p->wheel_width*5, p->wheel_width*5);
268 }
269 
270 qreal ColorWheel::hue() const
271 {
272  if ( (p->display_flags & COLOR_LCH) && p->sat > 0.01 )
273  return color().hueF();
274  return p->hue;
275 }
276 
277 qreal ColorWheel::saturation() const
278 {
279  return color().hsvSaturationF();
280 }
281 
282 qreal ColorWheel::value() const
283 {
284  return color().valueF();
285 }
286 
287 unsigned int ColorWheel::wheelWidth() const
288 {
289  return p->wheel_width;
290 }
291 
292 void ColorWheel::setWheelWidth(unsigned int w)
293 {
294  p->wheel_width = w;
296  update();
297 }
298 
299 void ColorWheel::paintEvent(QPaintEvent * )
300 {
301  QPainter painter(this);
302  painter.setRenderHint(QPainter::Antialiasing);
303  painter.translate(geometry().width()/2,geometry().height()/2);
304 
305  // hue wheel
306  if(p->hue_ring.isNull())
307  p->render_ring();
308 
309  painter.drawPixmap(-p->outer_radius(), -p->outer_radius(), p->hue_ring);
310 
311  // hue selector
312  painter.setPen(QPen(Qt::black,3));
313  painter.setBrush(Qt::NoBrush);
314  QLineF ray(0, 0, p->outer_radius(), 0);
315  ray.setAngle(p->hue*360);
316  QPointF h1 = ray.p2();
317  ray.setLength(p->inner_radius());
318  QPointF h2 = ray.p2();
319  painter.drawLine(h1,h2);
320 
321  // lum-sat square
322  if(p->inner_selector.isNull())
324 
325  painter.rotate(p->selector_image_angle());
326  painter.translate(p->selector_image_offset());
327 
328  QPointF selector_position;
329  if ( p->display_flags & SHAPE_SQUARE )
330  {
331  qreal side = p->square_size();
332  selector_position = QPointF(p->sat*side, p->val*side);
333  }
334  else if ( p->display_flags & SHAPE_TRIANGLE )
335  {
336  qreal side = p->triangle_side();
337  qreal height = p->triangle_height();
338  qreal slice_h = side * p->val;
339  qreal ymin = side/2-slice_h/2;
340 
341  selector_position = QPointF(p->val*height, ymin + p->sat*slice_h);
342  QPolygonF triangle;
343  triangle.append(QPointF(0,side/2));
344  triangle.append(QPointF(height,0));
345  triangle.append(QPointF(height,side));
346  QPainterPath clip;
347  clip.addPolygon(triangle);
348  painter.setClipPath(clip);
349  }
350 
351  painter.drawImage(QRectF(QPointF(0, 0), p->selector_size()), p->inner_selector);
352  painter.setClipping(false);
353 
354  // lum-sat selector
355  painter.setPen(QPen(p->val > 0.5 ? Qt::black : Qt::white, 3));
356  painter.setBrush(Qt::NoBrush);
357  painter.drawEllipse(selector_position, selector_radius, selector_radius);
358 
359 }
360 
361 void ColorWheel::mouseMoveEvent(QMouseEvent *ev)
362 {
363  if (p->mouse_status == DragCircle )
364  {
365  p->hue = p->line_to_point(ev->pos()).angle()/360.0;
367 
368  emit colorSelected(color());
369  emit colorChanged(color());
370  update();
371  }
372  else if(p->mouse_status == DragSquare)
373  {
374  QLineF glob_mouse_ln = p->line_to_point(ev->pos());
375  QLineF center_mouse_ln ( QPointF(0,0),
376  glob_mouse_ln.p2() - glob_mouse_ln.p1() );
377 
378  center_mouse_ln.setAngle(center_mouse_ln.angle()+p->selector_image_angle());
379  center_mouse_ln.setP2(center_mouse_ln.p2()-p->selector_image_offset());
380 
381  if ( p->display_flags & SHAPE_SQUARE )
382  {
383  p->sat = qBound(0.0, center_mouse_ln.x2()/p->square_size(), 1.0);
384  p->val = qBound(0.0, center_mouse_ln.y2()/p->square_size(), 1.0);
385  }
386  else if ( p->display_flags & SHAPE_TRIANGLE )
387  {
388  QPointF pt = center_mouse_ln.p2();
389 
390  qreal side = p->triangle_side();
391  p->val = qBound(0.0, pt.x() / p->triangle_height(), 1.0);
392  qreal slice_h = side * p->val;
393 
394  qreal ycenter = side/2;
395  qreal ymin = ycenter-slice_h/2;
396 
397  if ( slice_h > 0 )
398  p->sat = qBound(0.0, (pt.y()-ymin)/slice_h, 1.0);
399  }
400 
401  emit colorSelected(color());
402  emit colorChanged(color());
403  update();
404  }
405 }
406 
407 void ColorWheel::mousePressEvent(QMouseEvent *ev)
408 {
409  if ( ev->buttons() & Qt::LeftButton )
410  {
411  setFocus();
412  QLineF ray = p->line_to_point(ev->pos());
413  if ( ray.length() <= p->inner_radius() )
415  else if ( ray.length() <= p->outer_radius() )
417 
418  // Update the color
419  mouseMoveEvent(ev);
420  }
421 }
422 
423 void ColorWheel::mouseReleaseEvent(QMouseEvent *ev)
424 {
425  mouseMoveEvent(ev);
427 }
428 
429 void ColorWheel::resizeEvent(QResizeEvent *)
430 {
431  p->render_ring();
433 }
434 
436 {
437  qreal oldh = p->hue;
438  p->set_color(c);
439  if (!qFuzzyCompare(oldh+1, p->hue+1))
441  update();
442  emit colorChanged(c);
443 }
444 
445 void ColorWheel::setHue(qreal h)
446 {
447  p->hue = qBound(0.0, h, 1.0);
449  update();
450 }
451 
453 {
454  p->sat = qBound(0.0, s, 1.0);
455  update();
456 }
457 
458 void ColorWheel::setValue(qreal v)
459 {
460  p->val = qBound(0.0, v, 1.0);
461  update();
462 }
463 
464 
465 void ColorWheel::setDisplayFlags(DisplayFlags flags)
466 {
467  if ( ! (flags & COLOR_FLAGS) )
468  flags |= default_flags & COLOR_FLAGS;
469  if ( ! (flags & ANGLE_FLAGS) )
470  flags |= default_flags & ANGLE_FLAGS;
471  if ( ! (flags & SHAPE_FLAGS) )
472  flags |= default_flags & SHAPE_FLAGS;
473 
474  if ( (flags & COLOR_FLAGS) != (p->display_flags & COLOR_FLAGS) )
475  {
476  QColor old_col = color();
477  if ( flags & ColorWheel::COLOR_HSL )
478  {
479  p->hue = old_col.hueF();
480  p->sat = detail::color_HSL_saturationF(old_col);
481  p->val = detail::color_lightnessF(old_col);
484  }
485  else if ( flags & ColorWheel::COLOR_LCH )
486  {
487  p->hue = old_col.hueF();
488  p->sat = detail::color_chromaF(old_col);
489  p->val = detail::color_lumaF(old_col);
492  }
493  else
494  {
495  p->hue = old_col.hsvHueF();
496  p->sat = old_col.hsvSaturationF();
497  p->val = old_col.valueF();
498  p->color_from = &QColor::fromHsvF;
500  }
501  p->render_ring();
502  }
503 
504  p->display_flags = flags;
506  update();
507  emit displayFlagsChanged(flags);
508 }
509 
510 ColorWheel::DisplayFlags ColorWheel::displayFlags(DisplayFlags mask) const
511 {
512  return p->display_flags & mask;
513 }
514 
515 void ColorWheel::setDefaultDisplayFlags(DisplayFlags flags)
516 {
517  if ( !(flags & COLOR_FLAGS) )
518  flags |= hard_default_flags & COLOR_FLAGS;
519  if ( !(flags & ANGLE_FLAGS) )
520  flags |= hard_default_flags & ANGLE_FLAGS;
521  if ( !(flags & SHAPE_FLAGS) )
522  flags |= hard_default_flags & SHAPE_FLAGS;
523  default_flags = flags;
524 }
525 
526 ColorWheel::DisplayFlags ColorWheel::defaultDisplayFlags(DisplayFlags mask)
527 {
528  return default_flags & mask;
529 }
530 
531 void ColorWheel::setDisplayFlag(DisplayFlags flag, DisplayFlags mask)
532 {
533  setDisplayFlags((p->display_flags&~mask)|flag);
534 }
535 
536 void ColorWheel::dragEnterEvent(QDragEnterEvent* event)
537 {
538  if ( event->mimeData()->hasColor() ||
539  ( event->mimeData()->hasText() && QColor(event->mimeData()->text()).isValid() ) )
540  event->acceptProposedAction();
541 }
542 
543 void ColorWheel::dropEvent(QDropEvent* event)
544 {
545  if ( event->mimeData()->hasColor() )
546  {
547  setColor(event->mimeData()->colorData().value<QColor>());
548  event->accept();
549  }
550  else if ( event->mimeData()->hasText() )
551  {
552  QColor col(event->mimeData()->text());
553  if ( col.isValid() )
554  {
555  setColor(col);
556  event->accept();
557  }
558  }
559 }
560 
561 } // namespace color_widgets
Display an analog widget that allows the selection of a HSV color.
Definition: color_wheel.hpp:35
static ColorWheel::DisplayFlags default_flags
Definition: color_wheel.cpp:43
void render_inner_selector()
Updates the inner image that displays the saturation-value selector.
QColor rainbow_lch(qreal hue)
Definition: color_utils.hpp:42
qreal outer_radius() const
Calculate outer wheel radius from idget center.
Definition: color_wheel.cpp:70
qreal triangle_side() const
Calculate the side of the inner triangle.
Definition: color_wheel.cpp:94
Mask for the angle flags.
Definition: color_wheel.hpp:57
QColor rainbow_hsv(qreal hue)
Definition: color_utils.hpp:47
ColorWheel(QWidget *parent=0)
QPointF selector_image_offset()
Offset of the selector image.
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE
void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE
QSize sizeHint() const Q_DECL_OVERRIDE
void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE
void render_triangle()
renders the selector as a triangle
Use the HSL color space.
Definition: color_wheel.hpp:61
Use the HSV color space.
Definition: color_wheel.hpp:60
Mask for the shape flags.
Definition: color_wheel.hpp:52
static const double selector_radius
Definition: color_wheel.cpp:44
qreal square_size() const
Calculate the edge length of the inner square.
Definition: color_wheel.cpp:82
void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE
void setColor(QColor c)
Set current color.
qreal color_lumaF(const QColor &c)
Definition: color_utils.hpp:36
qreal triangle_height() const
Calculate the height of the inner triangle.
Definition: color_wheel.cpp:88
Definition: chrono.h:284
void setWheelWidth(unsigned int w)
Set the width in pixels of the outer wheel.
qreal saturation() const
Get current saturation in the range [0-1].
qreal color_chromaF(const QColor &c)
Definition: color_utils.hpp:29
void setDisplayFlags(ColorWheel::DisplayFlags flags)
Set the display flags.
QColor color_from_lch(qreal hue, qreal chroma, qreal luma, qreal alpha=1)
Definition: color_utils.cpp:28
qreal value() const
Get current value in the range [0-1].
void render_ring()
Updates the outer ring that displays the hue selector.
void setDisplayFlag(DisplayFlags flag, DisplayFlags mask)
Set a specific display flag.
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE
void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE
static DisplayFlags defaultDisplayFlags(DisplayFlags mask=FLAGS_ALL)
Get default display flags.
QColor(* color_from)(qreal, qreal, qreal, qreal)
Definition: color_wheel.cpp:58
void displayFlagsChanged(ColorWheel::DisplayFlags flags)
qreal color_HSL_saturationF(const QColor &col)
Definition: color_utils.hpp:58
QLineF line_to_point(const QPoint &p) const
return line from center to given point
static const ColorWheel::DisplayFlags hard_default_flags
Definition: color_wheel.cpp:42
MQTTClient c
Definition: test10.c:1656
Use Luma Chroma Hue (Y_601&#39;)
Definition: color_wheel.hpp:62
static void setDefaultDisplayFlags(DisplayFlags flags)
Set the default display flags.
qreal selector_image_angle()
Rotation of the selector image.
unsigned int wheelWidth() const
Get the width in pixels of the outer wheel.
QSizeF selector_size()
Size of the selector when rendered to the screen.
QColor color_from_hsl(qreal hue, qreal sat, qreal lig, qreal alpha=1)
Definition: color_utils.cpp:55
qreal inner_radius() const
Calculate inner wheel radius from idget center.
Definition: color_wheel.cpp:76
qreal color_lightnessF(const QColor &c)
Definition: color_utils.hpp:52
void set_color(const QColor &c)
QColor color() const
Get current color.
Mask for the color space flags.
Definition: color_wheel.hpp:63
qreal hue() const
Get current hue in the range [0-1].
The inner part follows the hue selector.
Definition: color_wheel.hpp:56
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:47:33