FlowVis 1.0

FlowVis/qtcolortriangle.cpp

00001 /****************************************************************************
00002 ** 
00003 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
00004 ** All rights reserved.
00005 ** Contact: Nokia Corporation (qt-info@nokia.com)
00006 ** 
00007 ** This file is part of a Qt Solutions component.
00008 **
00009 ** Commercial Usage  
00010 ** Licensees holding valid Qt Commercial licenses may use this file in
00011 ** accordance with the Qt Solutions Commercial License Agreement provided
00012 ** with the Software or, alternatively, in accordance with the terms
00013 ** contained in a written agreement between you and Nokia.
00014 ** 
00015 ** GNU Lesser General Public License Usage
00016 ** Alternatively, this file may be used under the terms of the GNU Lesser
00017 ** General Public License version 2.1 as published by the Free Software
00018 ** Foundation and appearing in the file LICENSE.LGPL included in the
00019 ** packaging of this file.  Please review the following information to
00020 ** ensure the GNU Lesser General Public License version 2.1 requirements
00021 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
00022 ** 
00023 ** In addition, as a special exception, Nokia gives you certain
00024 ** additional rights. These rights are described in the Nokia Qt LGPL
00025 ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this
00026 ** package.
00027 ** 
00028 ** GNU General Public License Usage 
00029 ** Alternatively, this file may be used under the terms of the GNU
00030 ** General Public License version 3.0 as published by the Free Software
00031 ** Foundation and appearing in the file LICENSE.GPL included in the
00032 ** packaging of this file.  Please review the following information to
00033 ** ensure the GNU General Public License version 3.0 requirements will be
00034 ** met: http://www.gnu.org/copyleft/gpl.html.
00035 ** 
00036 ** Please note Third Party Software included with Qt Solutions may impose
00037 ** additional restrictions and it is the user's responsibility to ensure
00038 ** that they have met the licensing requirements of the GPL, LGPL, or Qt
00039 ** Solutions Commercial license and the relevant license of the Third
00040 ** Party Software they are using.
00041 ** 
00042 ** If you are unsure which license is appropriate for your use, please
00043 ** contact Nokia at qt-info@nokia.com.
00044 ** 
00045 ****************************************************************************/
00046 
00047 #include "qtcolortriangle.h"
00048 
00049 #include <QtCore/QEvent>
00050 #include <QtCore/QMap>
00051 #include <QtCore/QVarLengthArray>
00052 #include <QtGui/QConicalGradient>
00053 #include <QtGui/QFrame>
00054 #include <QtGui/QImage>
00055 #include <QtGui/QKeyEvent>
00056 #include <QtGui/QLayout>
00057 #include <QtGui/QMouseEvent>
00058 #include <QtGui/QPainter>
00059 #include <QtGui/QPainterPath>
00060 #include <QtGui/QPixmap>
00061 #include <QtGui/QResizeEvent>
00062 #include <QtGui/QToolTip>
00063 #include <QtGui/QVBoxLayout>
00064 
00065 #include <math.h>
00066 
00090 const double PI = 3.14159265358979323846264338327950288419717;
00091 const double TWOPI = 2.0*PI;
00092 
00093 /*
00094     Used to store color values in the range 0..255 as doubles.
00095 */
00096 struct DoubleColor
00097 {
00098     double r, g, b;
00099 
00100     DoubleColor() : r(0.0), g(0.0), b(0.0) {}
00101     DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
00102     DoubleColor(const DoubleColor &c) : r(c.r), g(c.g), b(c.b) {}
00103 };
00104 
00105 /*
00106     Used to store pairs of DoubleColor and DoublePoint in one structure.
00107 */
00108 struct Vertex {
00109     DoubleColor color;
00110     QPointF point;
00111 
00112     Vertex(const DoubleColor &c, const QPointF &p) : color(c), point(p) {}
00113     Vertex(const QColor &c, const QPointF &p)
00114         : color(DoubleColor((double) c.red(), (double) c.green(),
00115                             (double) c.blue())), point(p) {}
00116 };
00117 
00122 static void swap(Vertex **a, Vertex **b)
00123 {
00124     Vertex *tmp = *a;
00125     *a = *b;
00126     *b = tmp;
00127 }
00128 
00132 QtColorTriangle::QtColorTriangle(QWidget *parent)
00133     : QWidget(parent), bg(sizeHint(), QImage::Format_RGB32), selMode(Idle)
00134 {
00135     setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
00136     setFocusPolicy(Qt::StrongFocus);
00137 
00138     mustGenerateBackground = true;
00139 
00140     QColor tmp;
00141     tmp.setHsv(76, 184, 206);
00142     setColor(tmp);
00143 }
00144 
00148 QtColorTriangle::~QtColorTriangle()
00149 {
00150 }
00151 
00157 void QtColorTriangle::polish()
00158 {
00159     outerRadius = (contentsRect().width() - 1) / 2;
00160     if ((contentsRect().height() - 1) / 2 < outerRadius)
00161         outerRadius = (contentsRect().height() - 1) / 2;
00162 
00163     penWidth = (int) floor(outerRadius / 50.0);
00164     ellipseSize = (int) floor(outerRadius / 12.5);
00165 
00166     double cx = (double) contentsRect().center().x();
00167     double cy = (double) contentsRect().center().y();
00168 
00169     pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00170                      cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00171     pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00172                      cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00173     pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00174                      cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00175     pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00176                      cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00177 
00178     // Find the current position of the selector
00179     selectorPos = pointFromColor(curColor);
00180 
00181     update();
00182 }
00183 
00186 QSize QtColorTriangle::sizeHint() const
00187 {
00188     return QSize(50, 50);
00189 }
00190 
00195 int QtColorTriangle::heightForWidth(int w) const
00196 {
00197     return w;
00198 }
00199 
00206 void QtColorTriangle::genBackground()
00207 {
00208     // Find the inner radius of the hue donut.
00209     double innerRadius = outerRadius - outerRadius / 5;
00210 
00211     // Create an image of the same size as the contents rect.
00212     bg = QImage(contentsRect().size(), QImage::Format_RGB32);
00213     QPainter p(&bg);
00214     p.setRenderHint(QPainter::Antialiasing);
00215     p.fillRect(bg.rect(), palette().background());
00216 
00217     QConicalGradient gradient(bg.rect().center(), 90);
00218     QColor color;
00219     for (double i = 0; i <= 1.0; i += 0.1) {
00220 #if QT_VERSION < 0x040100
00221         color.setHsv(int(i * 360.0), 255, 255);
00222 #else
00223         color.setHsv(int(360.0 - (i * 360.0)), 255, 255);
00224 #endif
00225         gradient.setColorAt(i, color);
00226     }
00227 
00228     QRectF innerRadiusRect(bg.rect().center().x() - innerRadius, bg.rect().center().y() - innerRadius,
00229                            innerRadius * 2 + 1, innerRadius * 2 + 1);
00230     QRectF outerRadiusRect(bg.rect().center().x() - outerRadius, bg.rect().center().y() - outerRadius,
00231                            outerRadius * 2 + 1, outerRadius * 2 + 1);
00232     QPainterPath path;
00233     path.addEllipse(innerRadiusRect);
00234     path.addEllipse(outerRadiusRect);
00235 
00236     p.save();
00237     p.setClipPath(path);
00238     p.fillRect(bg.rect(), gradient);
00239     p.restore();
00240 
00241     double penThickness = bg.width() / 400.0;
00242     for (int f = 0; f <= 5760; f += 20) {
00243         int value = int((0.5 + cos(((f - 1800) / 5760.0) * TWOPI) / 2) * 255.0);
00244 
00245         color.setHsv(int((f / 5760.0) * 360.0), 128 + (255 - value)/2, 255 - (255 - value)/4);
00246         p.setPen(QPen(color, penThickness));
00247         p.drawArc(innerRadiusRect, 1440 - f, 20);
00248         
00249         color.setHsv(int((f / 5760.0) * 360.0), 128 + value/2, 255 - value/4);
00250         p.setPen(QPen(color, penThickness));
00251         p.drawArc(outerRadiusRect, 2880 - 1440 - f, 20);
00252     }
00253     return;
00254 }
00255 
00262 void QtColorTriangle::mouseMoveEvent(QMouseEvent *e)
00263 {
00264     if ((e->buttons() & Qt::LeftButton) == 0)
00265         return;
00266 
00267     QPointF depos((double) e->pos().x(), (double) e->pos().y());
00268     bool newColor = false;
00269 
00270     if (selMode == SelectingHue) {
00271         // If selecting hue, find the new angles for the points a,b,c
00272         // of the triangle. The following update() will then redraw
00273         // the triangle.
00274         a = angleAt(depos, contentsRect());
00275         b = a + TWOPI / 3.0;
00276         c = b + TWOPI / 3.0;
00277         if (b > TWOPI) b -= TWOPI;
00278         if (c > TWOPI) c -= TWOPI;
00279 
00280         double am = a - PI/2;
00281         if (am < 0) am += TWOPI;
00282 
00283         curHue = 360 - (int) (((am) * 360.0) / TWOPI);
00284         int h,s,v;
00285         curColor.getHsv(&h, &s, &v);
00286 
00287         if (curHue != h) {
00288             newColor = true;
00289             curColor.setHsv(curHue, s, v);
00290         }
00291 
00292         double cx = (double) contentsRect().center().x();
00293         double cy = (double) contentsRect().center().y();
00294 
00295         pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00296                      cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00297         pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00298                      cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00299         pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00300                      cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00301         pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00302                      cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00303 
00304         selectorPos = pointFromColor(curColor);
00305     } else {
00306         Vertex aa(Qt::black, pa);
00307         Vertex bb(Qt::black, pb);
00308         Vertex cc(Qt::black, pc);
00309 
00310         Vertex *p1 = &aa;
00311         Vertex *p2 = &bb;
00312         Vertex *p3 = &cc;
00313         if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00314         if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00315         if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00316 
00317         selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00318         QColor col = colorFromPoint(selectorPos);
00319         if (col != curColor) {
00320             // Ensure that hue does not change when selecting
00321             // saturation and value.
00322             int h,s,v;
00323             col.getHsv(&h, &s, &v);
00324             curColor.setHsv(curHue, s, v);
00325             newColor = true;
00326         }
00327     }
00328 
00329     if (newColor)
00330         emit colorChanged(curColor);
00331 
00332     update();
00333 }
00334 
00343 void QtColorTriangle::mousePressEvent(QMouseEvent *e)
00344 {
00345     // Only respond to the left mouse button.
00346     if (e->button() != Qt::LeftButton)
00347         return;
00348 
00349     QPointF depos((double) e->pos().x(), (double) e->pos().y());
00350     double rad = radiusAt(depos, contentsRect());
00351     bool newColor = false;
00352 
00353     // As in mouseMoveEvent, either find the a,b,c angles or the
00354     // radian position of the selector, then order an update.
00355     if (rad > (outerRadius - (outerRadius / 5))) {
00356         selMode = SelectingHue;
00357 
00358         a = angleAt(depos, contentsRect());
00359         b = a + TWOPI / 3.0;
00360         c = b + TWOPI / 3.0;
00361         if (b > TWOPI) b -= TWOPI;
00362         if (c > TWOPI) c -= TWOPI;
00363 
00364         double am = a - PI/2;
00365         if (am < 0) am += TWOPI;
00366 
00367         curHue = 360 - (int) ((am * 360.0) / TWOPI);
00368         int h,s,v;
00369         curColor.getHsv(&h, &s, &v);
00370 
00371         if (h != curHue) {
00372             newColor = true;
00373             curColor.setHsv(curHue, s, v);
00374         }
00375 
00376         double cx = (double) contentsRect().center().x();
00377         double cy = (double) contentsRect().center().y();
00378 
00379         pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00380                          cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00381         pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00382                          cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00383         pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00384                          cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00385         pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00386                          cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00387 
00388         selectorPos = pointFromColor(curColor);
00389         emit colorChanged(curColor);
00390     } else {
00391         selMode = SelectingSatValue;
00392 
00393         Vertex aa(Qt::black, pa);
00394         Vertex bb(Qt::black, pb);
00395         Vertex cc(Qt::black, pc);
00396 
00397         Vertex *p1 = &aa;
00398         Vertex *p2 = &bb;
00399         Vertex *p3 = &cc;
00400         if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00401         if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00402         if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00403 
00404         selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00405         QColor col = colorFromPoint(selectorPos);
00406         if (col != curColor) {
00407             curColor = col;
00408             newColor = true;
00409         }
00410     }
00411 
00412     if (newColor)
00413         emit colorChanged(curColor);
00414 
00415     update();
00416 }
00417 
00423 void QtColorTriangle::mouseReleaseEvent(QMouseEvent *e)
00424 {
00425     if (e->button() == Qt::LeftButton)
00426         selMode = Idle;
00427 }
00428 
00432 void QtColorTriangle::keyPressEvent(QKeyEvent *e)
00433 {
00434     switch (e->key()) {
00435         case Qt::Key_Left: {
00436             --curHue;
00437             if (curHue < 0) curHue += 360;
00438             int h,s,v;
00439             curColor.getHsv(&h, &s, &v);
00440             QColor tmp;
00441             tmp.setHsv(curHue, s, v);
00442             setColor(tmp);
00443         }
00444             break;
00445         case Qt::Key_Right: {
00446             ++curHue;
00447             if (curHue > 359) curHue -= 360;
00448             int h,s,v;
00449             curColor.getHsv(&h, &s, &v);
00450             QColor tmp;
00451             tmp.setHsv(curHue, s, v);
00452             setColor(tmp);
00453         }
00454             break;
00455         case Qt::Key_Up: {
00456             int h,s,v;
00457             curColor.getHsv(&h, &s, &v);
00458             QColor tmp;
00459             if (e->modifiers() & Qt::ShiftModifier) {
00460                 if (s > 5) s -= 5;
00461                 else s = 0;
00462             } else {
00463                 if (v > 5) v -= 5;
00464                 else v = 0;
00465             }
00466             tmp.setHsv(curHue, s, v);
00467             setColor(tmp);
00468         }
00469             break;
00470         case Qt::Key_Down: {
00471             int h,s,v;
00472             curColor.getHsv(&h, &s, &v);
00473             QColor tmp;
00474             if (e->modifiers() & Qt::ShiftModifier) {
00475                 if (s < 250) s += 5;
00476                 else s = 255;
00477             } else {
00478                 if (v < 250) v += 5;
00479                 else v = 255;
00480             }
00481             tmp.setHsv(curHue, s, v);
00482             setColor(tmp);
00483         }
00484             break;
00485     };
00486 }
00487 
00493 void QtColorTriangle::resizeEvent(QResizeEvent *)
00494 {
00495     outerRadius = (contentsRect().width() - 1) / 2;
00496     if ((contentsRect().height() - 1) / 2 < outerRadius)
00497         outerRadius = (contentsRect().height() - 1) / 2;
00498 
00499     penWidth = (int) floor(outerRadius / 50.0);
00500     ellipseSize = (int) floor(outerRadius / 12.5);
00501 
00502     double cx = (double) contentsRect().center().x();
00503     double cy = (double) contentsRect().center().y();
00504 
00505     pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00506                  cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00507     pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00508                  cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00509     pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00510                  cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00511     pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00512                  cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00513 
00514     // Find the current position of the selector
00515     selectorPos = pointFromColor(curColor);
00516 
00517     mustGenerateBackground = true;
00518     update();
00519 }
00520 
00527 void QtColorTriangle::paintEvent(QPaintEvent *e)
00528 {
00529     QPainter p(this);
00530     if (e->rect().intersects(contentsRect()))
00531         p.setClipRegion(e->region().intersect(contentsRect()));
00532     if (mustGenerateBackground) {
00533         genBackground();
00534         mustGenerateBackground = false;
00535     }
00536 
00537     // Blit the static generated background with the hue gradient onto
00538     // the double buffer.
00539     QImage buf = bg.copy();
00540 
00541     // Draw the trigon
00542     int h,s,v;
00543     curColor.getHsv(&h, &s, &v);
00544 
00545     // Find the color with only the hue, and max value and saturation
00546     QColor hueColor;
00547     hueColor.setHsv(curHue, 255, 255);
00548 
00549     // Draw the triangle
00550     drawTrigon(&buf, pa, pb, pc, hueColor);
00551 
00552     // Slow step: convert the image to a pixmap
00553     QPixmap pix = QPixmap::fromImage(buf);
00554     QPainter painter(&pix);
00555     painter.setRenderHint(QPainter::Antialiasing);
00556 
00557     // Draw an outline of the triangle
00558     QColor halfAlpha(0, 0, 0, 128);
00559     painter.setPen(QPen(halfAlpha, 0));
00560     painter.drawLine(pa, pb);
00561     painter.drawLine(pb, pc);
00562     painter.drawLine(pc, pa);
00563     
00564     int ri, gi, bi;
00565     hueColor.getRgb(&ri, &gi, &bi);
00566     if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00567         painter.setPen(QPen(Qt::black, penWidth));
00568     else
00569         painter.setPen(QPen(Qt::white, penWidth));
00570     painter.drawEllipse((int) (pd.x() - ellipseSize / 2.0),
00571                         (int) (pd.y() - ellipseSize / 2.0),
00572                         ellipseSize, ellipseSize);
00573 
00574     curColor.getRgb(&ri, &gi, &bi);
00575 
00576     // Find a color for painting the selector based on the brightness
00577     // value of the color.
00578     if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00579         painter.setPen(QPen(Qt::black, penWidth));
00580     else
00581         painter.setPen(QPen(Qt::white, penWidth));
00582 
00583     // Draw the selector ellipse.
00584     painter.drawEllipse(QRectF(selectorPos.x() - ellipseSize / 2.0,
00585                                selectorPos.y() - ellipseSize / 2.0,
00586                                ellipseSize + 0.5, ellipseSize + 0.5));
00587 
00588     // Blit
00589     p.drawPixmap(contentsRect().topLeft(), pix);
00590 }
00591 
00601 void QtColorTriangle::drawTrigon(QImage *buf, const QPointF &pa,
00602                                const QPointF &pb, const QPointF &pc,
00603                                const QColor &color)
00604 {
00605     // Create three Vertex objects. A Vertex contains a double-point
00606     // coordinate and a color.
00607     // pa is the tip of the arrow
00608     // pb is the black corner
00609     // pc is the white corner
00610     Vertex aa(color, pa);
00611     Vertex bb(Qt::black, pb);
00612     Vertex cc(Qt::white, pc);
00613 
00614     // Sort. Make p1 above p2, which is above p3 (using y coordinate).
00615     // Bubble sorting is fastest here.
00616     Vertex *p1 = &aa;
00617     Vertex *p2 = &bb;
00618     Vertex *p3 = &cc;
00619     if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00620     if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00621     if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00622 
00623     // All the three y deltas are >= 0
00624     double p1p2ydist = p2->point.y() - p1->point.y();
00625     double p1p3ydist = p3->point.y() - p1->point.y();
00626     double p2p3ydist = p3->point.y() - p2->point.y();
00627     double p1p2xdist = p2->point.x() - p1->point.x();
00628     double p1p3xdist = p3->point.x() - p1->point.x();
00629     double p2p3xdist = p3->point.x() - p2->point.x();
00630 
00631     // The first x delta decides wether we have a lefty or a righty
00632     // trigon.
00633     bool lefty = p1p2xdist < 0;
00634 
00635     // Left and right colors and X values. The key in this map is the
00636     // y values. Our goal is to fill these structures with all the
00637     // information needed to do a single pass top-to-bottom,
00638     // left-to-right drawing of the trigon.
00639     QVarLengthArray<DoubleColor, 2000> leftColors;
00640     QVarLengthArray<DoubleColor, 2000> rightColors;
00641     QVarLengthArray<double, 2000> leftX; 
00642     QVarLengthArray<double, 2000> rightX; 
00643 
00644     leftColors.resize(int(floor(p3->point.y() + 1)));
00645     rightColors.resize(int(floor(p3->point.y() + 1)));
00646     leftX.resize(int(floor(p3->point.y() + 1)));
00647     rightX.resize(int(floor(p3->point.y() + 1)));
00648 
00649      // Scan longy - find all left and right colors and X-values for
00650     // the tallest edge (p1-p3).
00651     DoubleColor source;
00652     DoubleColor dest;
00653     double r, g, b;
00654     double rdelta, gdelta, bdelta;
00655     double x;
00656     double xdelta;
00657     int y1, y2;
00658 
00659     // Initialize with known values
00660     x = p1->point.x();
00661     source = p1->color;
00662     dest = p3->color;
00663     r = source.r;
00664     g = source.g;
00665     b = source.b;
00666     y1 = (int) floor(p1->point.y());
00667     y2 = (int) floor(p3->point.y());
00668 
00669     // Find slopes (notice that if the y dists are 0, we don't care
00670     // about the slopes)
00671     xdelta = p1p3ydist == 0.0 ? 0.0 : p1p3xdist / p1p3ydist;
00672     rdelta = p1p3ydist == 0.0 ? 0.0 : (dest.r - r) / p1p3ydist;
00673     gdelta = p1p3ydist == 0.0 ? 0.0 : (dest.g - g) / p1p3ydist;
00674     bdelta = p1p3ydist == 0.0 ? 0.0 : (dest.b - b) / p1p3ydist;
00675 
00676     // Calculate gradients using linear approximation
00677     int y;
00678     for (y = y1; y < y2; ++y) {
00679         if (lefty) {
00680             rightColors[y] = DoubleColor(r, g, b);
00681             rightX[y] = x;
00682         } else {
00683             leftColors[y] = DoubleColor(r, g, b);
00684             leftX[y] = x;
00685         }
00686 
00687         r += rdelta;
00688         g += gdelta;
00689         b += bdelta;
00690         x += xdelta;
00691     }
00692 
00693     // Scan top shorty - find all left and right colors and x-values
00694     // for the topmost of the two not-tallest short edges.
00695     x = p1->point.x();
00696     source = p1->color;
00697     dest = p2->color;
00698     r = source.r;
00699     g = source.g;
00700     b = source.b;
00701     y1 = (int) floor(p1->point.y());
00702     y2 = (int) floor(p2->point.y());
00703 
00704     // Find slopes (notice that if the y dists are 0, we don't care
00705     // about the slopes)
00706     xdelta = p1p2ydist == 0.0 ? 0.0 : p1p2xdist / p1p2ydist;
00707     rdelta = p1p2ydist == 0.0 ? 0.0 : (dest.r - r) / p1p2ydist;
00708     gdelta = p1p2ydist == 0.0 ? 0.0 : (dest.g - g) / p1p2ydist;
00709     bdelta = p1p2ydist == 0.0 ? 0.0 : (dest.b - b) / p1p2ydist;
00710 
00711     // Calculate gradients using linear approximation
00712     for (y = y1; y < y2; ++y) {
00713         if (lefty) {
00714             leftColors[y] = DoubleColor(r, g, b);
00715             leftX[y] = x;
00716         } else {
00717             rightColors[y] = DoubleColor(r, g, b);
00718             rightX[y] = x;
00719         }
00720 
00721         r += rdelta;
00722         g += gdelta;
00723         b += bdelta;
00724         x += xdelta;
00725     }
00726 
00727     // Scan bottom shorty - find all left and right colors and
00728     // x-values for the bottommost of the two not-tallest short edges.
00729     x = p2->point.x();
00730     source = p2->color;
00731     dest = p3->color;
00732     r = source.r;
00733     g = source.g;
00734     b = source.b;
00735     y1 = (int) floor(p2->point.y());
00736     y2 = (int) floor(p3->point.y());
00737 
00738     // Find slopes (notice that if the y dists are 0, we don't care
00739     // about the slopes)
00740     xdelta = p2p3ydist == 0.0 ? 0.0 : p2p3xdist / p2p3ydist;
00741     rdelta = p2p3ydist == 0.0 ? 0.0 : (dest.r - r) / p2p3ydist;
00742     gdelta = p2p3ydist == 0.0 ? 0.0 : (dest.g - g) / p2p3ydist;
00743     bdelta = p2p3ydist == 0.0 ? 0.0 : (dest.b - b) / p2p3ydist;
00744 
00745     // Calculate gradients using linear approximation
00746     for (y = y1; y < y2; ++y) {
00747         if (lefty) {
00748             leftColors[y] = DoubleColor(r, g, b);
00749             leftX[y] = x;
00750         } else {
00751             rightColors[y] = DoubleColor(r, g, b);
00752             rightX[y] = x;
00753         }
00754 
00755         r += rdelta;
00756         g += gdelta;
00757         b += bdelta;
00758         x += xdelta;
00759     }
00760 
00761     // Inner loop. For each y in the left map of x-values, draw one
00762     // line from left to right.
00763     const int p3yfloor = int(floor(p3->point.y()));
00764     for (int y = int(floor(p1->point.y())); y < p3yfloor; ++y) {
00765         double lx = leftX[y];
00766         double rx = rightX[y];
00767 
00768         int lxi = (int) floor(lx);
00769         int rxi = (int) floor(rx);
00770         DoubleColor rc = rightColors[y];
00771         DoubleColor lc = leftColors[y];
00772 
00773         // if the xdist is 0, don't draw anything.
00774         double xdist = rx - lx;
00775         if (xdist != 0.0) {
00776             double r = lc.r;
00777             double g = lc.g;
00778             double b = lc.b;
00779             double rdelta = (rc.r - r) / xdist;
00780             double gdelta = (rc.g - g) / xdist;
00781             double bdelta = (rc.b - b) / xdist;
00782 
00783             QRgb *scanline = reinterpret_cast<QRgb *>(buf->scanLine(y));
00784             scanline += lxi;
00785 
00786             // Inner loop 2. Draws the line from left to right.
00787             for (int i = lxi; i < rxi; ++i) {
00788                 *scanline++ = qRgb((int) r, (int) g, (int) b);
00789                 r += rdelta;
00790                 g += gdelta;
00791                 b += bdelta;
00792             }
00793         }
00794     }
00795 }
00796 
00801 void QtColorTriangle::setColor(const QColor &col)
00802 {
00803     if (col == curColor)
00804         return;
00805 
00806     curColor = col;
00807 
00808     int h, s, v;
00809     curColor.getHsv(&h, &s, &v);
00810 
00811     // Never use an invalid hue to display colors
00812     if (h != -1)
00813         curHue = h;
00814 
00815     a = (((360 - curHue) * TWOPI) / 360.0);
00816     a += PI / 2.0;
00817     if (a > TWOPI) a -= TWOPI;
00818 
00819     b = a + TWOPI/3;
00820     c = b + TWOPI/3;
00821 
00822     if (b > TWOPI) b -= TWOPI;
00823     if (c > TWOPI) c -= TWOPI;
00824 
00825     double cx = (double) contentsRect().center().x();
00826     double cy = (double) contentsRect().center().y();
00827     double innerRadius = outerRadius - (outerRadius / 5.0);
00828     double pointerRadius = outerRadius - (outerRadius / 10.0);
00829 
00830     pa = QPointF(cx + (cos(a) * innerRadius), cy - (sin(a) * innerRadius));
00831     pb = QPointF(cx + (cos(b) * innerRadius), cy - (sin(b) * innerRadius));
00832     pc = QPointF(cx + (cos(c) * innerRadius), cy - (sin(c) * innerRadius));
00833     pd = QPointF(cx + (cos(a) * pointerRadius), cy - (sin(a) * pointerRadius));
00834 
00835     selectorPos = pointFromColor(curColor);
00836     update();
00837 
00838     emit colorChanged(curColor);
00839 }
00840 
00845 QColor QtColorTriangle::color() const
00846 {
00847     return curColor;
00848 }
00849 
00855 double QtColorTriangle::radiusAt(const QPointF &pos, const QRect &rect) const
00856 {
00857     double mousexdist = pos.x() - (double) rect.center().x();
00858     double mouseydist = pos.y() - (double) rect.center().y();
00859     return sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00860 }
00861 
00869 double QtColorTriangle::angleAt(const QPointF &pos, const QRect &rect) const
00870 {
00871     double mousexdist = pos.x() - (double) rect.center().x();
00872     double mouseydist = pos.y() - (double) rect.center().y();
00873     double mouserad = sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00874     if (mouserad == 0.0)
00875         return 0.0;
00876 
00877     double angle = acos(mousexdist / mouserad);
00878     if (mouseydist >= 0)
00879         angle = TWOPI - angle;
00880 
00881     return angle;
00882 }
00883 
00888 inline double qsqr(double a)
00889 {
00890     return a * a;
00891 }
00892 
00897 inline double vlen(double x, double y)
00898 {
00899     return sqrt(qsqr(x) + qsqr(y));
00900 }
00901 
00906 inline double vprod(double x1, double y1, double x2, double y2)
00907 {
00908     return x1 * x2 + y1 * y2;
00909 }
00910 
00916 bool angleBetweenAngles(double p, double a1, double a2)
00917 {
00918     if (a1 > a2) {
00919         a2 += TWOPI;
00920         if (p < PI) p += TWOPI;
00921     }
00922 
00923     return p >= a1 && p < a2;
00924 }
00925 
00943 static bool pointAbovePoint(double x, double y, double px, double py,
00944                             double ax, double ay, double bx, double by)
00945 {
00946     bool result = false;
00947 
00948     if (floor(ax) > floor(bx)) {
00949         if (floor(ay) < floor(by)) {
00950             // line is draw upright-to-downleft
00951             if (floor(x) < floor(px) || floor(y) < floor(py))
00952                 result = true;
00953         } else if (floor(ay) > floor(by)) {
00954             // line is draw downright-to-upleft
00955             if (floor(x) > floor(px) || floor(y) < floor(py))
00956                 result = true;
00957         } else {
00958             // line is flat horizontal
00959             if (y < ay) result = true;
00960         }
00961     } else if (floor(ax) < floor(bx)) {
00962         if (floor(ay) < floor(by)) {
00963             // line is draw upleft-to-downright
00964             if (floor(x) < floor(px) || floor(y) > floor(py))
00965                 result = true;
00966         } else if (floor(ay) > floor(by)) {
00967             // line is draw downleft-to-upright
00968             if (floor(x) > floor(px) || floor(y) > floor(py))
00969                 result = true;
00970         } else {
00971             // line is flat horizontal
00972             if (y > ay)
00973                 result = true;
00974         }
00975     } else {
00976         // line is vertical
00977         if (floor(ay) < floor(by)) {
00978             if (x < ax) result = true;
00979         } else if (floor(ay) > floor(by)) {
00980             if (x > ax) result = true;
00981         } else {
00982             if (!(x == ax && y == ay))
00983                 result = true;
00984         }
00985     }
00986 
00987     return result;
00988 }
00989 
00997 static int pointInLine(double x, double y, double ax, double ay,
00998                        double bx, double by)
00999 {
01000     if (ax > bx) {
01001         if (ay < by) {
01002             // line is draw upright-to-downleft
01003 
01004             // if (x,y) is in on or above the upper right point,
01005             // return -1.
01006             if (y <= ay && x >= ax)
01007                 return -1;
01008 
01009             // if (x,y) is in on or below the lower left point,
01010             // return 1.
01011             if (y >= by && x <= bx)
01012                 return 1;
01013         } else {
01014             // line is draw downright-to-upleft
01015 
01016             // If the line is flat, only use the x coordinate.
01017             if (floor(ay) == floor(by)) {
01018                 // if (x is to the right of the rightmost point,
01019                 // return -1. otherwise if x is to the left of the
01020                 // leftmost point, return 1.
01021                 if (x >= ax)
01022                     return -1;
01023                 else if (x <= bx)
01024                     return 1;
01025             } else {
01026                 // if (x,y) is on or below the lower right point,
01027                 // return -1.
01028                 if (y >= ay && x >= ax)
01029                     return -1;
01030 
01031                 // if (x,y) is on or above the upper left point,
01032                 // return 1.
01033                 if (y <= by && x <= bx)
01034                     return 1;
01035             }
01036         }
01037     } else {
01038         if (ay < by) {
01039             // line is draw upleft-to-downright
01040 
01041             // If (x,y) is on or above the upper left point, return
01042             // -1.
01043             if (y <= ay && x <= ax)
01044                 return -1;
01045 
01046             // If (x,y) is on or below the lower right point, return
01047             // 1.
01048             if (y >= by && x >= bx)
01049                 return 1;
01050         } else {
01051             // line is draw downleft-to-upright
01052 
01053             // If the line is flat, only use the x coordinate.
01054             if (floor(ay) == floor(by)) {
01055                 if (x <= ax)
01056                     return -1;
01057                 else if (x >= bx)
01058                     return 1;
01059             } else {
01060                 // If (x,y) is on or below the lower left point, return
01061                 // -1.
01062                 if (y >= ay && x <= ax)
01063                     return -1;
01064 
01065                 // If (x,y) is on or above the upper right point, return
01066                 // 1.
01067                 if (y <= by && x >= bx)
01068                     return 1;
01069             }
01070         }
01071     }
01072 
01073     // No tests proved that (x,y) was outside [(ax,ay),(bx,by)], so we
01074     // assume it's inside the line's bounds.
01075     return 0;
01076 }
01077 
01093 QPointF QtColorTriangle::movePointToTriangle(double x, double y, const Vertex &a,
01094                                                const Vertex &b, const Vertex &c) const
01095 {
01096     // Let v1A be the vector from (x,y) to a.
01097     // Let v2A be the vector from a to b.
01098     // Find the angle alphaA between v1A and v2A.
01099     double v1xA = x - a.point.x();
01100     double v1yA = y - a.point.y();
01101     double v2xA = b.point.x() - a.point.x();
01102     double v2yA = b.point.y() - a.point.y();
01103     double vpA = vprod(v1xA, v1yA, v2xA, v2yA);
01104     double cosA = vpA / (vlen(v1xA, v1yA) * vlen(v2xA, v2yA));
01105     double alphaA = acos(cosA);
01106 
01107     // Let v1B be the vector from x to b.
01108     // Let v2B be the vector from b to c.
01109     double v1xB = x - b.point.x();
01110     double v1yB = y - b.point.y();
01111     double v2xB = c.point.x() - b.point.x();
01112     double v2yB = c.point.y() - b.point.y();
01113     double vpB = vprod(v1xB, v1yB, v2xB, v2yB);
01114     double cosB = vpB / (vlen(v1xB, v1yB) * vlen(v2xB, v2yB));
01115     double alphaB = acos(cosB);
01116 
01117     // Let v1C be the vector from x to c.
01118     // Let v2C be the vector from c back to a.
01119     double v1xC = x - c.point.x();
01120     double v1yC = y - c.point.y();
01121     double v2xC = a.point.x() - c.point.x();
01122     double v2yC = a.point.y() - c.point.y();
01123     double vpC = vprod(v1xC, v1yC, v2xC, v2yC);
01124     double cosC = vpC / (vlen(v1xC, v1yC) * vlen(v2xC, v2yC));
01125     double alphaC = acos(cosC);
01126 
01127     // Find the radian angles between the (1,0) vector and the points
01128     // A, B, C and (x,y). Use this information to determine which of
01129     // the edges we should project (x,y) onto.
01130     double angleA = angleAt(a.point, contentsRect());
01131     double angleB = angleAt(b.point, contentsRect());
01132     double angleC = angleAt(c.point, contentsRect());
01133     double angleP = angleAt(QPointF(x, y), contentsRect());
01134 
01135     // If (x,y) is in the a-b area, project onto the a-b vector.
01136     if (angleBetweenAngles(angleP, angleA, angleB)) {
01137         // Find the distance from (x,y) to a. Then use the slope of
01138         // the a-b vector with this distance and the angle between a-b
01139         // and a-(x,y) to determine the point of intersection of the
01140         // perpendicular projection from (x,y) onto a-b.
01141         double pdist = sqrt(qsqr(x - a.point.x()) + qsqr(y - a.point.y()));
01142 
01143         // the length of all edges is always > 0
01144         double p0x = a.point.x() + ((b.point.x() - a.point.x()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01145         double p0y = a.point.y() + ((b.point.y() - a.point.y()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01146 
01147         // If (x,y) is above the a-b line, which basically means it's
01148         // outside the triangle, then return its projection onto a-b.
01149         if (pointAbovePoint(x, y, p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y())) {
01150             // If the projection is "outside" a, return a. If it is
01151             // outside b, return b. Otherwise return the projection.
01152             int n = pointInLine(p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y());
01153             if (n < 0)
01154                 return a.point;
01155             else if (n > 0)
01156                 return b.point;
01157 
01158             return QPointF(p0x, p0y);
01159         }
01160     } else if (angleBetweenAngles(angleP, angleB, angleC)) {
01161         // If (x,y) is in the b-c area, project onto the b-c vector.
01162         double pdist = sqrt(qsqr(x - b.point.x()) + qsqr(y - b.point.y()));
01163 
01164         // the length of all edges is always > 0
01165         double p0x = b.point.x() + ((c.point.x() - b.point.x()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01166         double p0y = b.point.y() + ((c.point.y() - b.point.y()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01167 
01168         if (pointAbovePoint(x, y, p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y())) {
01169             int n = pointInLine(p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y());
01170             if (n < 0)
01171                 return b.point;
01172             else if (n > 0)
01173                 return c.point;
01174             return QPointF(p0x, p0y);
01175         }
01176     } else if (angleBetweenAngles(angleP, angleC, angleA)) {
01177         // If (x,y) is in the c-a area, project onto the c-a vector.
01178         double pdist = sqrt(qsqr(x - c.point.x()) + qsqr(y - c.point.y()));
01179 
01180         // the length of all edges is always > 0
01181         double p0x = c.point.x() + ((a.point.x() - c.point.x()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01182         double p0y = c.point.y() + ((a.point.y() - c.point.y()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01183 
01184         if (pointAbovePoint(x, y, p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y())) {
01185             int n = pointInLine(p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y());
01186             if (n < 0)
01187                 return c.point;
01188             else if (n > 0)
01189                 return a.point;
01190             return QPointF(p0x, p0y);
01191         }
01192     }
01193 
01194     // (x,y) is inside the triangle (inside a-b, b-c and a-c).
01195     return QPointF(x, y);
01196 }
01197 
01212 QPointF QtColorTriangle::pointFromColor(const QColor &col) const
01213 {
01214     // Simplifications for the corner cases.
01215     if (col == Qt::black)
01216         return pb;
01217     else if (col == Qt::white)
01218         return pc;
01219 
01220     // Find the x and y slopes
01221     double ab_deltax = pb.x() - pa.x();
01222     double ab_deltay = pb.y() - pa.y();
01223     double bc_deltax = pc.x() - pb.x();
01224     double bc_deltay = pc.y() - pb.y();
01225     double ac_deltax = pc.x() - pa.x();
01226     double ac_deltay = pc.y() - pa.y();
01227 
01228     // Extract the h,s,v values of col.
01229     int hue,sat,val;
01230     col.getHsv(&hue, &sat, &val);
01231 
01232     // Find the line that passes through the triangle where the value
01233     // is equal to our color's value.
01234     double p1 = pa.x() + (ab_deltax * (double) (255 - val)) / 255.0;
01235     double q1 = pa.y() + (ab_deltay * (double) (255 - val)) / 255.0;
01236     double p2 = pb.x() + (bc_deltax * (double) val) / 255.0;
01237     double q2 = pb.y() + (bc_deltay * (double) val) / 255.0;
01238 
01239     // Find the line that passes through the triangle where the
01240     // saturation is equal to our color's value.
01241     double p3 = pa.x() + (ac_deltax * (double) (255 - sat)) / 255.0;
01242     double q3 = pa.y() + (ac_deltay * (double) (255 - sat)) / 255.0;
01243     double p4 = pb.x();
01244     double q4 = pb.y();
01245 
01246     // Find the intersection between these lines.
01247         double x = 0;
01248         double y = 0;
01249         if (p1 != p2) {
01250                 double a = (q2 - q1) / (p2 - p1);
01251                 double c = (q4 - q3) / (p4 - p3);
01252                 double b = q1 - a * p1;
01253                 double d = q3 - c * p3;
01254 
01255                 x = (d - b) / (a - c);
01256                 y = a * x + b;
01257         }
01258         else {
01259                 x = p1;
01260                 y = q3 + (x - p3) * (q4 - q3) / (p4 - p3);
01261         }
01262         
01263     return QPointF(x, y);
01264 }
01265 
01273 QColor QtColorTriangle::colorFromPoint(const QPointF &p) const
01274 {
01275     // Find the outer radius of the hue gradient.
01276     int outerRadius = (contentsRect().width() - 1) / 2;
01277     if ((contentsRect().height() - 1) / 2 < outerRadius)
01278         outerRadius = (contentsRect().height() - 1) / 2;
01279 
01280     // Find the center coordinates
01281     double cx = (double) contentsRect().center().x();
01282     double cy = (double) contentsRect().center().y();
01283 
01284     // Find the a, b and c from their angles, the center of the rect
01285     // and the radius of the hue gradient donut.
01286     QPointF pa(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
01287                    cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
01288     QPointF pb(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
01289                    cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
01290     QPointF pc(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
01291                    cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
01292 
01293     // Find the hue value from the angle of the 'a' point.
01294     double angle = a - PI/2.0;
01295     if (angle < 0) angle += TWOPI;
01296     double hue = (360.0 * angle) / TWOPI;
01297 
01298     // Create the color of the 'a' corner point. We know that b is
01299     // black and c is white.
01300     QColor color;
01301     color.setHsv(360 - (int) floor(hue), 255, 255);
01302 
01303     // See also drawTrigon(), which basically does exactly the same to
01304     // determine all colors in the trigon.
01305     Vertex aa(color, pa);
01306     Vertex bb(Qt::black, pb);
01307     Vertex cc(Qt::white, pc);
01308 
01309     // Make sure p1 is above p2, which is above p3.
01310     Vertex *p1 = &aa;
01311     Vertex *p2 = &bb;
01312     Vertex *p3 = &cc;
01313     if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
01314     if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
01315     if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
01316 
01317     // Find the slopes of all edges in the trigon. All the three y
01318     // deltas here are positive because of the above sorting.
01319     double p1p2ydist = p2->point.y() - p1->point.y();
01320     double p1p3ydist = p3->point.y() - p1->point.y();
01321     double p2p3ydist = p3->point.y() - p2->point.y();
01322     double p1p2xdist = p2->point.x() - p1->point.x();
01323     double p1p3xdist = p3->point.x() - p1->point.x();
01324     double p2p3xdist = p3->point.x() - p2->point.x();
01325 
01326     // The first x delta decides wether we have a lefty or a righty
01327     // trigon. A lefty trigon has its tallest edge on the right hand
01328     // side of the trigon. The righty trigon has it on its left side.
01329     // This property determines wether the left or the right set of x
01330     // coordinates will be continuous.
01331     bool lefty = p1p2xdist < 0;
01332 
01333     // Find whether the selector's y is in the first or second shorty,
01334     // counting from the top and downwards. This is used to find the
01335     // color at the selector point.
01336     bool firstshorty = (p.y() >= p1->point.y() && p.y() < p2->point.y());
01337 
01338     // From the y value of the selector's position, find the left and
01339     // right x values.
01340     double leftx;
01341     double rightx;
01342     if (lefty) {
01343         if (firstshorty) {
01344             leftx = p1->point.x();
01345             if (floor(p1p2ydist) != 0.0) {
01346                 leftx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01347             } else {
01348                 leftx = qMin(p1->point.x(), p2->point.x());
01349             }
01350         } else {
01351             leftx = p2->point.x();
01352             if (floor(p2p3ydist) != 0.0) {
01353                 leftx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01354             } else {
01355                 leftx = qMin(p2->point.x(), p3->point.x());
01356             }
01357         }
01358 
01359         rightx = p1->point.x();
01360         rightx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01361     } else {
01362         leftx = p1->point.x();
01363         leftx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01364 
01365         if (firstshorty) {
01366             rightx = p1->point.x();
01367             if (floor(p1p2ydist) != 0.0) {
01368                 rightx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01369             } else {
01370                 rightx = qMax(p1->point.x(), p2->point.x());
01371             }
01372         } else {
01373             rightx = p2->point.x(); 
01374             if (floor(p2p3ydist) != 0.0) {
01375                 rightx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01376             } else {
01377                 rightx = qMax(p2->point.x(), p3->point.x());
01378             }
01379         }
01380     }
01381 
01382     // Find the r,g,b values of the points on the trigon's edges that
01383     // are to the left and right of the selector.
01384     double rshort = 0, gshort = 0, bshort = 0;
01385     double rlong = 0, glong = 0, blong = 0;
01386     if (firstshorty) {
01387         if (floor(p1p2ydist) != 0.0) {
01388             rshort  = p2->color.r * (p.y() - p1->point.y()) / p1p2ydist;
01389             gshort  = p2->color.g * (p.y() - p1->point.y()) / p1p2ydist;
01390             bshort  = p2->color.b * (p.y() - p1->point.y()) / p1p2ydist;
01391             rshort += p1->color.r * (p2->point.y() - p.y()) / p1p2ydist;
01392             gshort += p1->color.g * (p2->point.y() - p.y()) / p1p2ydist;
01393             bshort += p1->color.b * (p2->point.y() - p.y()) / p1p2ydist;
01394         } else {
01395             if (lefty) {
01396                 if (p1->point.x() <= p2->point.x()) {
01397                     rshort  = p1->color.r;
01398                     gshort  = p1->color.g;
01399                     bshort  = p1->color.b;
01400                 } else {
01401                     rshort  = p2->color.r;
01402                     gshort  = p2->color.g;
01403                     bshort  = p2->color.b;
01404                 }
01405             } else {
01406                 if (p1->point.x() > p2->point.x()) {
01407                     rshort  = p1->color.r;
01408                     gshort  = p1->color.g;
01409                     bshort  = p1->color.b;
01410                 } else {
01411                     rshort  = p2->color.r;
01412                     gshort  = p2->color.g;
01413                     bshort  = p2->color.b;
01414                 }
01415             }
01416         }
01417     } else {
01418         if (floor(p2p3ydist) != 0.0) {
01419             rshort  = p3->color.r * (p.y() - p2->point.y()) / p2p3ydist;
01420             gshort  = p3->color.g * (p.y() - p2->point.y()) / p2p3ydist;
01421             bshort  = p3->color.b * (p.y() - p2->point.y()) / p2p3ydist;
01422             rshort += p2->color.r * (p3->point.y() - p.y()) / p2p3ydist;
01423             gshort += p2->color.g * (p3->point.y() - p.y()) / p2p3ydist;
01424             bshort += p2->color.b * (p3->point.y() - p.y()) / p2p3ydist;
01425         } else {
01426             if (lefty) {
01427                 if (p2->point.x() <= p3->point.x()) {
01428                     rshort  = p2->color.r;
01429                     gshort  = p2->color.g;
01430                     bshort  = p2->color.b;
01431                 } else {
01432                     rshort  = p3->color.r;
01433                     gshort  = p3->color.g;
01434                     bshort  = p3->color.b;
01435                 }
01436             } else {
01437                 if (p2->point.x() > p3->point.x()) {
01438                     rshort  = p2->color.r;
01439                     gshort  = p2->color.g;
01440                     bshort  = p2->color.b;
01441                 } else {
01442                     rshort  = p3->color.r;
01443                     gshort  = p3->color.g;
01444                     bshort  = p3->color.b;
01445                 }
01446             }
01447         }
01448     }
01449 
01450     // p1p3ydist is never 0
01451     rlong  = p3->color.r * (p.y() - p1->point.y()) / p1p3ydist;
01452     glong  = p3->color.g * (p.y() - p1->point.y()) / p1p3ydist;
01453     blong  = p3->color.b * (p.y() - p1->point.y()) / p1p3ydist;
01454     rlong += p1->color.r * (p3->point.y() - p.y()) / p1p3ydist;
01455     glong += p1->color.g * (p3->point.y() - p.y()) / p1p3ydist;
01456     blong += p1->color.b * (p3->point.y() - p.y()) / p1p3ydist;
01457 
01458     // rshort,gshort,bshort is the color on one of the shortys.
01459     // rlong,glong,blong is the color on the longy. So depending on
01460     // wether we have a lefty trigon or not, we can determine which
01461     // colors are on the left and right edge.
01462     double rl, gl, bl, rr, gr, br;
01463     if (lefty) {
01464         rl = rshort; gl = gshort; bl = bshort;
01465         rr = rlong; gr = glong; br = blong;
01466     } else {
01467         rl = rlong; gl = glong; bl = blong;
01468         rr = rshort; gr = gshort; br = bshort;
01469     }
01470 
01471     // Find the distance from the left x to the right x (xdist). Then
01472     // find the distances from the selector to each of these (saxdist
01473     // and saxdist2). These distances are used to find the color at
01474     // the selector.
01475     double xdist = rightx - leftx;
01476     double saxdist = p.x() - leftx;
01477     double saxdist2 = xdist - saxdist;
01478 
01479     // Now determine the r,g,b values of the selector using a linear
01480     // approximation.
01481     double r, g, b;
01482     if (xdist != 0.0) {
01483         r = (saxdist2 * rl / xdist) + (saxdist * rr / xdist);
01484         g = (saxdist2 * gl / xdist) + (saxdist * gr / xdist);
01485         b = (saxdist2 * bl / xdist) + (saxdist * br / xdist);
01486     } else {
01487         // In theory, the left and right color will be equal here. But
01488         // because of the loss of precision, we get an error on both
01489         // colors. The best approximation we can get is from adding
01490         // the two errors, which in theory will eliminate the error
01491         // but in practise will only minimize it.
01492         r = (rl + rr) / 2;
01493         g = (gl + gr) / 2;
01494         b = (bl + br) / 2;
01495     }
01496 
01497     // Now floor the color components and fit them into proper
01498     // boundaries. This again is to compensate for the error caused by
01499     // loss of precision.
01500     int ri = (int) floor(r);
01501     int gi = (int) floor(g);
01502     int bi = (int) floor(b);
01503     if (ri < 0) ri = 0;
01504     else if (ri > 255) ri = 255;
01505     if (gi < 0) gi = 0;
01506     else if (gi > 255) gi = 255;
01507     if (bi < 0) bi = 0;
01508     else if (bi > 255) bi = 255;
01509 
01510     // Voila, we have the color at the point of the selector.
01511     return QColor(ri, gi, bi);
01512 }
 All Classes Functions Variables Friends