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