color_wheel.cpp
Go to the documentation of this file.
00001 
00022 #include "color_wheel.hpp"
00023 
00024 #include <cmath>
00025 #include <QMouseEvent>
00026 #include <QPainter>
00027 #include <QLineF>
00028 #include <QDragEnterEvent>
00029 #include <QMimeData>
00030 #include "color_utils.hpp"
00031 
00032 namespace color_widgets {
00033 
00034 enum MouseStatus
00035 {
00036     Nothing,
00037     DragCircle,
00038     DragSquare
00039 };
00040 
00041 static const ColorWheel::DisplayFlags hard_default_flags = ColorWheel::SHAPE_TRIANGLE|ColorWheel::ANGLE_ROTATING|ColorWheel::COLOR_HSV;
00042 static ColorWheel::DisplayFlags default_flags = hard_default_flags;
00043 static const double selector_radius = 6;
00044 
00045 class ColorWheel::Private
00046 {
00047 private:
00048     ColorWheel * const w;
00049 
00050 public:
00051     qreal hue, sat, val;
00052     unsigned int wheel_width;
00053     MouseStatus mouse_status;
00054     QPixmap hue_ring;
00055     QImage inner_selector;
00056     DisplayFlags display_flags;
00057     QColor (*color_from)(qreal,qreal,qreal,qreal);
00058     QColor (*rainbow_from_hue)(qreal);
00059     int max_size = 128;
00060 
00061     Private(ColorWheel *widget)
00062         : w(widget), hue(0), sat(0), val(0),
00063         wheel_width(20), mouse_status(Nothing),
00064         display_flags(FLAGS_DEFAULT),
00065         color_from(&QColor::fromHsvF), rainbow_from_hue(&detail::rainbow_hsv)
00066     { }
00067 
00069     qreal outer_radius() const
00070     {
00071         return qMin(w->geometry().width(), w->geometry().height())/2;
00072     }
00073 
00075     qreal inner_radius() const
00076     {
00077         return outer_radius()-wheel_width;
00078     }
00079 
00081     qreal square_size() const
00082     {
00083         return inner_radius()*qSqrt(2);
00084     }
00085 
00087     qreal triangle_height() const
00088     {
00089         return inner_radius()*3/2;
00090     }
00091 
00093     qreal triangle_side() const
00094     {
00095         return inner_radius()*qSqrt(3);
00096     }
00097 
00099     QLineF line_to_point(const QPoint &p) const
00100     {
00101         return QLineF (w->geometry().width()/2, w->geometry().height()/2, p.x(), p.y());
00102     }
00103 
00104     void render_square()
00105     {
00106         int width = qMin<int>(square_size(), max_size);
00107         QSize size(width, width);
00108         inner_selector = QImage(size, QImage::Format_RGB32);
00109 
00110         for ( int y = 0; y < width; ++y )
00111         {
00112             for ( int x = 0; x < width; ++x )
00113             {
00114                 inner_selector.setPixel( x, y,
00115                         color_from(hue,double(x)/width,double(y)/width,1).rgb());
00116             }
00117         }
00118     }
00119 
00124     void render_triangle()
00125     {
00126         QSizeF size = selector_size();
00127         if ( size.height() > max_size )
00128             size *= max_size / size.height();
00129 
00130         qreal ycenter = size.height()/2;
00131         inner_selector = QImage(size.toSize(), QImage::Format_RGB32);
00132 
00133         for (int x = 0; x < inner_selector.width(); x++ )
00134         {
00135             qreal pval = x / size.height();
00136             qreal slice_h = size.height() * pval;
00137             for (int y = 0; y < inner_selector.height(); y++ )
00138             {
00139                 qreal ymin = ycenter-slice_h/2;
00140                 qreal psat = qBound(0.0,(y-ymin)/slice_h,1.0);
00141 
00142                 inner_selector.setPixel(x,y,color_from(hue,psat,pval,1).rgb());
00143             }
00144         }
00145     }
00146 
00148     void render_inner_selector()
00149     {
00150         if ( display_flags & ColorWheel::SHAPE_TRIANGLE )
00151             render_triangle();
00152         else
00153             render_square();
00154     }
00155 
00157     QPointF selector_image_offset()
00158     {
00159         if ( display_flags & SHAPE_TRIANGLE )
00160                 return QPointF(-inner_radius(),-triangle_side()/2);
00161         return QPointF(-square_size()/2,-square_size()/2);
00162     }
00163 
00167     QSizeF selector_size()
00168     {
00169         if ( display_flags & SHAPE_TRIANGLE )
00170                 return QSizeF(triangle_height(), triangle_side());
00171         return QSizeF(square_size(), square_size());
00172     }
00173 
00174 
00176     qreal selector_image_angle()
00177     {
00178         if ( display_flags & SHAPE_TRIANGLE )
00179         {
00180             if ( display_flags & ANGLE_ROTATING )
00181                 return -hue*360-60;
00182             return -150;
00183         }
00184         else
00185         {
00186             if ( display_flags & ANGLE_ROTATING )
00187                 return -hue*360-45;
00188             else
00189                 return 180;
00190         }
00191     }
00192 
00194     void render_ring()
00195     {
00196         hue_ring = QPixmap(outer_radius()*2,outer_radius()*2);
00197         hue_ring.fill(Qt::transparent);
00198         QPainter painter(&hue_ring);
00199         painter.setRenderHint(QPainter::Antialiasing);
00200         painter.setCompositionMode(QPainter::CompositionMode_Source);
00201 
00202 
00203         const int hue_stops = 24;
00204         QConicalGradient gradient_hue(0, 0, 0);
00205         if ( gradient_hue.stops().size() < hue_stops )
00206         {
00207             for ( double a = 0; a < 1.0; a+=1.0/(hue_stops-1) )
00208             {
00209                 gradient_hue.setColorAt(a,rainbow_from_hue(a));
00210             }
00211             gradient_hue.setColorAt(1,rainbow_from_hue(0));
00212         }
00213 
00214         painter.translate(outer_radius(),outer_radius());
00215 
00216         painter.setPen(Qt::NoPen);
00217         painter.setBrush(QBrush(gradient_hue));
00218         painter.drawEllipse(QPointF(0,0),outer_radius(),outer_radius());
00219 
00220         painter.setBrush(Qt::transparent);//palette().background());
00221         painter.drawEllipse(QPointF(0,0),inner_radius(),inner_radius());
00222     }
00223 
00224     void set_color(const QColor& c)
00225     {
00226         if ( display_flags & ColorWheel::COLOR_HSV )
00227         {
00228             hue = qMax(0.0, c.hsvHueF());
00229             sat = c.hsvSaturationF();
00230             val = c.valueF();
00231         }
00232         else if ( display_flags & ColorWheel::COLOR_HSL )
00233         {
00234             hue = qMax(0.0, c.hueF());
00235             sat = detail::color_HSL_saturationF(c);
00236             val = detail::color_lightnessF(c);
00237         }
00238         else if ( display_flags & ColorWheel::COLOR_LCH )
00239         {
00240             hue = qMax(0.0, c.hsvHueF());
00241             sat = detail::color_chromaF(c);
00242             val = detail::color_lumaF(c);
00243         }
00244     }
00245 };
00246 
00247 ColorWheel::ColorWheel(QWidget *parent) :
00248     QWidget(parent), p(new Private(this))
00249 {
00250     setDisplayFlags(FLAGS_DEFAULT);
00251     setAcceptDrops(true);
00252 }
00253 
00254 ColorWheel::~ColorWheel()
00255 {
00256     delete p;
00257 }
00258 
00259 QColor ColorWheel::color() const
00260 {
00261     return p->color_from(p->hue, p->sat, p->val, 1);
00262 }
00263 
00264 QSize ColorWheel::sizeHint() const
00265 {
00266     return QSize(p->wheel_width*5, p->wheel_width*5);
00267 }
00268 
00269 qreal ColorWheel::hue() const
00270 {
00271     if ( (p->display_flags & COLOR_LCH) && p->sat > 0.01 )
00272         return color().hueF();
00273     return p->hue;
00274 }
00275 
00276 qreal ColorWheel::saturation() const
00277 {
00278     return color().hsvSaturationF();
00279 }
00280 
00281 qreal ColorWheel::value() const
00282 {
00283     return color().valueF();
00284 }
00285 
00286 unsigned int ColorWheel::wheelWidth() const
00287 {
00288     return p->wheel_width;
00289 }
00290 
00291 void ColorWheel::setWheelWidth(unsigned int w)
00292 {
00293     p->wheel_width = w;
00294     p->render_inner_selector();
00295     update();
00296 }
00297 
00298 void ColorWheel::paintEvent(QPaintEvent * )
00299 {
00300     QPainter painter(this);
00301     painter.setRenderHint(QPainter::Antialiasing);
00302     painter.translate(geometry().width()/2,geometry().height()/2);
00303 
00304     // hue wheel
00305     if(p->hue_ring.isNull())
00306         p->render_ring();
00307 
00308     painter.drawPixmap(-p->outer_radius(), -p->outer_radius(), p->hue_ring);
00309 
00310     // hue selector
00311     painter.setPen(QPen(Qt::black,3));
00312     painter.setBrush(Qt::NoBrush);
00313     QLineF ray(0, 0, p->outer_radius(), 0);
00314     ray.setAngle(p->hue*360);
00315     QPointF h1 = ray.p2();
00316     ray.setLength(p->inner_radius());
00317     QPointF h2 = ray.p2();
00318     painter.drawLine(h1,h2);
00319 
00320     // lum-sat square
00321     if(p->inner_selector.isNull())
00322         p->render_inner_selector();
00323 
00324     painter.rotate(p->selector_image_angle());
00325     painter.translate(p->selector_image_offset());
00326 
00327     QPointF selector_position;
00328     if ( p->display_flags & SHAPE_SQUARE )
00329     {
00330         qreal side = p->square_size();
00331         selector_position = QPointF(p->sat*side, p->val*side);
00332     }
00333     else if ( p->display_flags & SHAPE_TRIANGLE )
00334     {
00335         qreal side = p->triangle_side();
00336         qreal height = p->triangle_height();
00337         qreal slice_h = side * p->val;
00338         qreal ymin = side/2-slice_h/2;
00339 
00340         selector_position = QPointF(p->val*height, ymin + p->sat*slice_h);
00341         QPolygonF triangle;
00342         triangle.append(QPointF(0,side/2));
00343         triangle.append(QPointF(height,0));
00344         triangle.append(QPointF(height,side));
00345         QPainterPath clip;
00346         clip.addPolygon(triangle);
00347         painter.setClipPath(clip);
00348     }
00349 
00350     painter.drawImage(QRectF(QPointF(0, 0), p->selector_size()), p->inner_selector);
00351     painter.setClipping(false);
00352 
00353     // lum-sat selector
00354     painter.setPen(QPen(p->val > 0.5 ? Qt::black : Qt::white, 3));
00355     painter.setBrush(Qt::NoBrush);
00356     painter.drawEllipse(selector_position, selector_radius, selector_radius);
00357 
00358 }
00359 
00360 void ColorWheel::mouseMoveEvent(QMouseEvent *ev)
00361 {
00362     if (p->mouse_status == DragCircle )
00363     {
00364         p->hue = p->line_to_point(ev->pos()).angle()/360.0;
00365         p->render_inner_selector();
00366 
00367         emit colorSelected(color());
00368         emit colorChanged(color());
00369         update();
00370     }
00371     else if(p->mouse_status == DragSquare)
00372     {
00373         QLineF glob_mouse_ln = p->line_to_point(ev->pos());
00374         QLineF center_mouse_ln ( QPointF(0,0),
00375                                  glob_mouse_ln.p2() - glob_mouse_ln.p1() );
00376 
00377         center_mouse_ln.setAngle(center_mouse_ln.angle()+p->selector_image_angle());
00378         center_mouse_ln.setP2(center_mouse_ln.p2()-p->selector_image_offset());
00379 
00380         if ( p->display_flags & SHAPE_SQUARE )
00381         {
00382             p->sat = qBound(0.0, center_mouse_ln.x2()/p->square_size(), 1.0);
00383             p->val = qBound(0.0, center_mouse_ln.y2()/p->square_size(), 1.0);
00384         }
00385         else if ( p->display_flags & SHAPE_TRIANGLE )
00386         {
00387             QPointF pt = center_mouse_ln.p2();
00388 
00389             qreal side = p->triangle_side();
00390             p->val = qBound(0.0, pt.x() / p->triangle_height(), 1.0);
00391             qreal slice_h = side * p->val;
00392 
00393             qreal ycenter = side/2;
00394             qreal ymin = ycenter-slice_h/2;
00395 
00396             if ( slice_h > 0 )
00397                 p->sat = qBound(0.0, (pt.y()-ymin)/slice_h, 1.0);
00398         }
00399 
00400         emit colorSelected(color());
00401         emit colorChanged(color());
00402         update();
00403     }
00404 }
00405 
00406 void ColorWheel::mousePressEvent(QMouseEvent *ev)
00407 {
00408     if ( ev->buttons() & Qt::LeftButton )
00409     {
00410         setFocus();
00411         QLineF ray = p->line_to_point(ev->pos());
00412         if ( ray.length() <= p->inner_radius() )
00413             p->mouse_status = DragSquare;
00414         else if ( ray.length() <= p->outer_radius() )
00415             p->mouse_status = DragCircle;
00416 
00417         // Update the color
00418         mouseMoveEvent(ev);
00419     }
00420 }
00421 
00422 void ColorWheel::mouseReleaseEvent(QMouseEvent *ev)
00423 {
00424     mouseMoveEvent(ev);
00425     p->mouse_status = Nothing;
00426 }
00427 
00428 void ColorWheel::resizeEvent(QResizeEvent *)
00429 {
00430     p->render_ring();
00431     p->render_inner_selector();
00432 }
00433 
00434 void ColorWheel::setColor(QColor c)
00435 {
00436     qreal oldh = p->hue;
00437     p->set_color(c);
00438     if (!qFuzzyCompare(oldh+1, p->hue+1))
00439         p->render_inner_selector();
00440     update();
00441     emit colorChanged(c);
00442 }
00443 
00444 void ColorWheel::setHue(qreal h)
00445 {
00446     p->hue = qBound(0.0, h, 1.0);
00447     p->render_inner_selector();
00448     update();
00449 }
00450 
00451 void ColorWheel::setSaturation(qreal s)
00452 {
00453     p->sat = qBound(0.0, s, 1.0);
00454     update();
00455 }
00456 
00457 void ColorWheel::setValue(qreal v)
00458 {
00459     p->val = qBound(0.0, v, 1.0);
00460     update();
00461 }
00462 
00463 
00464 void ColorWheel::setDisplayFlags(DisplayFlags flags)
00465 {
00466     if ( ! (flags & COLOR_FLAGS) )
00467         flags |= default_flags & COLOR_FLAGS;
00468     if ( ! (flags & ANGLE_FLAGS) )
00469         flags |= default_flags & ANGLE_FLAGS;
00470     if ( ! (flags & SHAPE_FLAGS) )
00471         flags |= default_flags & SHAPE_FLAGS;
00472 
00473     if ( (flags & COLOR_FLAGS) != (p->display_flags & COLOR_FLAGS) )
00474     {
00475         QColor old_col = color();
00476         if ( flags & ColorWheel::COLOR_HSL )
00477         {
00478             p->hue = old_col.hueF();
00479             p->sat = detail::color_HSL_saturationF(old_col);
00480             p->val = detail::color_lightnessF(old_col);
00481             p->color_from = &detail::color_from_hsl;
00482             p->rainbow_from_hue = &detail::rainbow_hsv;
00483         }
00484         else if ( flags & ColorWheel::COLOR_LCH )
00485         {
00486             p->hue = old_col.hueF();
00487             p->sat = detail::color_chromaF(old_col);
00488             p->val = detail::color_lumaF(old_col);
00489             p->color_from = &detail::color_from_lch;
00490             p->rainbow_from_hue = &detail::rainbow_lch;
00491         }
00492         else
00493         {
00494             p->hue = old_col.hsvHueF();
00495             p->sat = old_col.hsvSaturationF();
00496             p->val = old_col.valueF();
00497             p->color_from = &QColor::fromHsvF;
00498             p->rainbow_from_hue = &detail::rainbow_hsv;
00499         }
00500         p->render_ring();
00501     }
00502 
00503     p->display_flags = flags;
00504     p->render_inner_selector();
00505     update();
00506     emit displayFlagsChanged(flags);
00507 }
00508 
00509 ColorWheel::DisplayFlags ColorWheel::displayFlags(DisplayFlags mask) const
00510 {
00511     return p->display_flags & mask;
00512 }
00513 
00514 void ColorWheel::setDefaultDisplayFlags(DisplayFlags flags)
00515 {
00516     if ( !(flags & COLOR_FLAGS) )
00517         flags |= hard_default_flags & COLOR_FLAGS;
00518     if ( !(flags & ANGLE_FLAGS) )
00519         flags |= hard_default_flags & ANGLE_FLAGS;
00520     if ( !(flags & SHAPE_FLAGS) )
00521         flags |= hard_default_flags & SHAPE_FLAGS;
00522     default_flags = flags;
00523 }
00524 
00525 ColorWheel::DisplayFlags ColorWheel::defaultDisplayFlags(DisplayFlags mask)
00526 {
00527     return default_flags & mask;
00528 }
00529 
00530 void ColorWheel::setDisplayFlag(DisplayFlags flag, DisplayFlags mask)
00531 {
00532     setDisplayFlags((p->display_flags&~mask)|flag);
00533 }
00534 
00535 void ColorWheel::dragEnterEvent(QDragEnterEvent* event)
00536 {
00537     if ( event->mimeData()->hasColor() ||
00538          ( event->mimeData()->hasText() && QColor(event->mimeData()->text()).isValid() ) )
00539         event->acceptProposedAction();
00540 }
00541 
00542 void ColorWheel::dropEvent(QDropEvent* event)
00543 {
00544     if ( event->mimeData()->hasColor() )
00545     {
00546         setColor(event->mimeData()->colorData().value<QColor>());
00547         event->accept();
00548     }
00549     else if ( event->mimeData()->hasText() )
00550     {
00551         QColor col(event->mimeData()->text());
00552         if ( col.isValid() )
00553         {
00554             setColor(col);
00555             event->accept();
00556         }
00557     }
00558 }
00559 
00560 } //  namespace color_widgets


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56